{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "from torch import nn\n",
    "from torch.nn import functional as F\n",
    "import numpy as np\n",
    "from numba import jit\n",
    "from matplotlib import pyplot as plt\n",
    "import time\n",
    "from datetime import timedelta\n",
    "import pandas as pd\n",
    "from collections import OrderedDict\n",
    "from itertools import cycle\n",
    "from typing import Optional, Tuple\n",
    "from transformers import AutoTokenizer\n",
    "from sentencepiece import SentencePieceProcessor\n",
    "from datasets import load_dataset\n",
    "\n",
    "device = \"cuda:0\" if torch.cuda.is_available() else \"cpu\"\n",
    "device_cap = torch.cuda.get_device_capability()\n",
    "device_type = \"cuda\" if \"cuda\" in device else \"cpu\"\n",
    "torch.cuda.set_device(device)\n",
    "torch.manual_seed(8855)\n",
    "print(torch.__version__)\n",
    "print(device, device_cap)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Llama 3 from Scratch"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Using Huggingface\n",
    "tokenizer = AutoTokenizer.from_pretrained(\"./llama3/\")\n",
    "#tokenizer.add_special_tokens({\"pad_token\": \"<PAD>\"})\n",
    "tokenizer.pad_token = tokenizer.eos_token #Optional\n",
    "\n",
    "vocab = tokenizer.vocab"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def encode(example):\n",
    "    return tokenizer.encode(example, return_tensors=\"pt\")\n",
    "\n",
    "\n",
    "def decode(example):\n",
    "    return tokenizer.batch_decode(\n",
    "        example,\n",
    "        skip_special_tokens=False,\n",
    "        clean_up_tokenization_spaces=True,\n",
    "    )[0]\n",
    "\n",
    "\n",
    "print(f\"Vocab Size: {len(vocab)}\")\n",
    "decode(\n",
    "    encode(\n",
    "        \"hello I am a specifically designed long sentence to make sure this is working not only adequately, but good enough for our batch functions\"\n",
    "    )\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "MASTER_CONFIG = {\n",
    "    \"vocab_size\": len(vocab),\n",
    "    \"batch_size\": 16,\n",
    "    \"context_window\": 32,\n",
    "    \"d_model\": 288,\n",
    "    \"epochs\": 1000,\n",
    "    \"hidden_dim\": 768,\n",
    "    \"log_interval\": 50,\n",
    "    \"n_heads\": 6,\n",
    "    \"n_layers\": 6,\n",
    "}\n",
    "\n",
    "GLOBAL_KEEP_TRACK = []"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Local Data only\n",
    "dataset = load_dataset(\n",
    "    \"text\",\n",
    "    data_files={\n",
    "        \"train\": [\"../../data/CombinedTinyStories-train.txt\"],\n",
    "        \"val\": [\"../../data/CombinedTinyStories-valid.txt\"],\n",
    "    },\n",
    "    streaming=True,\n",
    ")\n",
    "\n",
    "# Stream Data\n",
    "# dataset = load_dataset('IMJONEZZ/CombinedTinyStories') # optional: streaming=True but to_iterable is faster\n",
    "# dataset = dataset.to_iterable_dataset()\n",
    "\n",
    "# Minimal preprocessing\n",
    "clean_dataset = dataset.filter(lambda example: len(example[\"text\"]) > 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "prompt = \"Write a short story. Possible Story: \"\n",
    "tokenized_prompt = tokenizer(prompt, return_tensors=\"pt\").input_ids\n",
    "\n",
    "encoded_dataset = clean_dataset.map(\n",
    "    lambda examples: tokenizer(\n",
    "        [prompt + x for x in examples[\"text\"]],\n",
    "        padding=True,\n",
    "        return_tensors=\"pt\",\n",
    "    ),\n",
    "    batched=True,\n",
    ")\n",
    "train_data = iter(encoded_dataset[\"train\"].shuffle())\n",
    "val_data = iter(encoded_dataset[\"val\"].shuffle())\n",
    "train_data = cycle(train_data)\n",
    "val_data = cycle(val_data)\n",
    "\n",
    "test = next(train_data)\n",
    "print(f\"Actual text: {test['text']}\")\n",
    "print(f\"Input Ids: {tokenizer.decode(test['input_ids'])}\")\n",
    "print(f\"Length of text: {len(test['input_ids'])}\")\n",
    "print(test)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Needed functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# @torch.compile #For non-Windows users\n",
    "def get_batches(\n",
    "    data,\n",
    "    batch_size,\n",
    "    context_window,\n",
    "    config=MASTER_CONFIG,\n",
    "    debug=False,\n",
    "):\n",
    "    x = []\n",
    "    y = []\n",
    "    for _ in range(\n",
    "        batch_size\n",
    "    ):  # Adjust this lower if you're running out of memory\n",
    "        batch_data = next(data)\n",
    "\n",
    "        # pick random starting points\n",
    "        ix = torch.randint(\n",
    "            0, len(batch_data[\"input_ids\"]) - context_window - 1, (2,)\n",
    "        )\n",
    "        batch_x = torch.stack(\n",
    "            [batch_data[\"input_ids\"][i : i + context_window] for i in ix]\n",
    "        ).long()\n",
    "        batch_y = torch.stack(\n",
    "            [\n",
    "                batch_data[\"input_ids\"][i + 1 : i + context_window + 1]\n",
    "                for i in ix\n",
    "            ]\n",
    "        ).long()\n",
    "        x.append(batch_x)\n",
    "        y.append(batch_y)\n",
    "    x = torch.cat((x), 0).to(device)\n",
    "    y = torch.cat((y), 0).to(device)\n",
    "    if debug:\n",
    "        print(f\"ix: {ix}\\nx: {x}\\ny: {y}\")\n",
    "    return x, y\n",
    "\n",
    "\n",
    "def get_lora_batches(\n",
    "    data,\n",
    "    batch_size,\n",
    "    context_window=32,\n",
    "):\n",
    "    x = []\n",
    "    y = []\n",
    "    for _ in range(batch_size // 2):\n",
    "        x_data = next(data)\n",
    "        y_data = next(data)\n",
    "\n",
    "        x_data = torch.stack([x_data[\"input_ids\"]]).long()\n",
    "        y_data = torch.stack([y_data[\"input_ids\"]]).long()\n",
    "\n",
    "        x.append(x_data)\n",
    "        y.append(y_data)\n",
    "    x = torch.cat((x), 0).to(device)\n",
    "    y = torch.cat((y), 0).to(device)\n",
    "    return x, y\n",
    "\n",
    "xs, ys = get_batches(\n",
    "                    train_data, 16, 32\n",
    "                )\n",
    "print(xs)\n",
    "print(ys)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "@torch.no_grad()\n",
    "def get_loss(model, lora=False, config=MASTER_CONFIG):\n",
    "    out = {}\n",
    "    model.eval()\n",
    "    for name, split in zip([\"train\", \"val\"], [train_data, val_data]):\n",
    "        losses = []\n",
    "        for _ in range(10):\n",
    "            if lora == True:\n",
    "                xb, yb = get_lora_batches(\n",
    "                    split, config[\"batch_size\"], config[\"context_window\"]\n",
    "                )\n",
    "            else:\n",
    "                xb, yb = get_batches(\n",
    "                    split,\n",
    "                    config[\"batch_size\"],\n",
    "                    config[\"context_window\"],\n",
    "                )\n",
    "            _, loss = model(xb, yb)\n",
    "            losses.append(loss.item())\n",
    "        out[name] = np.mean(losses)\n",
    "    model.train()\n",
    "    return out\n",
    "\n",
    "\n",
    "@torch.inference_mode()\n",
    "def generate(\n",
    "    model,\n",
    "    config=MASTER_CONFIG,\n",
    "    temperature=1.0,\n",
    "    top_k=None,\n",
    "    max_new_tokens=30,\n",
    "    lora=False,\n",
    "):\n",
    "    idx_list = [tokenized_prompt] * 5\n",
    "    idx = torch.cat((idx_list), 0).long().to(device)\n",
    "    if lora:\n",
    "        idx = (\n",
    "            torch.ones(5, 1).long().to(device)\n",
    "        )  # Alternative without prompt\n",
    "    for _ in range(max_new_tokens):\n",
    "        # call the model\n",
    "        logits = model(idx[:, -config[\"context_window\"] :])\n",
    "        last_time_step_logits = logits[\n",
    "            :, -1, :\n",
    "        ]  # all the batches (1), last time step, all the logits\n",
    "\n",
    "        last_time_step_logits = last_time_step_logits / temperature\n",
    "        if top_k is not None:\n",
    "            v, _ = torch.topk(\n",
    "                last_time_step_logits,\n",
    "                min(top_k, last_time_step_logits.size(-1)),\n",
    "            )\n",
    "            last_time_step_logits[last_time_step_logits < v[:, [-1]]] = (\n",
    "                -float(\"Inf\")\n",
    "            )\n",
    "        p = F.softmax(\n",
    "            last_time_step_logits, dim=-1\n",
    "        )  # softmax to get probabilities\n",
    "        idx_next = torch.multinomial(\n",
    "            p, num_samples=1\n",
    "        )  # sample from the distribution to get the next token\n",
    "        idx = torch.cat([idx, idx_next], dim=-1)  # append to the sequence\n",
    "    return [tokenizer.decode(x) for x in idx.tolist()]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Two-layer Feed Forward Network"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class SimpleFeedForwardNN(nn.Module):\n",
    "    def __init__(self, config=MASTER_CONFIG):\n",
    "        super().__init__()\n",
    "        self.config = config\n",
    "\n",
    "        self.embedding = nn.Embedding(\n",
    "            config[\"vocab_size\"], config[\"d_model\"]\n",
    "        )\n",
    "        self.linear = nn.Sequential(\n",
    "            nn.Linear(config[\"d_model\"], config[\"d_model\"]),\n",
    "            nn.ReLU(),\n",
    "            nn.Linear(config[\"d_model\"], config[\"vocab_size\"]),\n",
    "        )\n",
    "\n",
    "        print(\n",
    "            f\"model params: {sum([m.numel() for m in self.parameters()])}\"\n",
    "        )\n",
    "\n",
    "    def forward(self, idx, targets=None):\n",
    "        x = self.embedding(idx)\n",
    "        logits = self.linear(x)\n",
    "\n",
    "        if targets is not None:\n",
    "            loss = F.cross_entropy(\n",
    "                logits.view(-1, self.config[\"vocab_size\"]),\n",
    "                targets.view(-1),\n",
    "                ignore_index=tokenizer.pad_token_id,\n",
    "                # reduction=\"sum\",\n",
    "            )\n",
    "            return logits, loss\n",
    "\n",
    "        else:\n",
    "            return logits\n",
    "\n",
    "\n",
    "model = SimpleFeedForwardNN(MASTER_CONFIG).to(device)\n",
    "# opt_model = torch.compile(model) #Again, non-Windows folks should be compiling functions and models\n",
    "# xs, ys = get_batches(\n",
    "#     train_data,\n",
    "#     MASTER_CONFIG[\"batch_size\"],\n",
    "#     MASTER_CONFIG[\"context_window\"],\n",
    "# )\n",
    "\n",
    "# logits, loss = model(xs, ys)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Training Loop"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def train(\n",
    "    model,\n",
    "    optimizer,\n",
    "    scheduler=None,\n",
    "    data=None,\n",
    "    config=MASTER_CONFIG,\n",
    "    lora=False,\n",
    "    print_logs=False,\n",
    "):\n",
    "    losses = []\n",
    "    start_time = time.time()\n",
    "    for epoch in range(config[\"epochs\"]):\n",
    "        try:\n",
    "            optimizer.zero_grad()\n",
    "\n",
    "            if lora:\n",
    "                xs, ys = get_lora_batches(data, config[\"batch_size\"])\n",
    "            else:\n",
    "                xs, ys = get_batches(\n",
    "                    data, config[\"batch_size\"], config[\"context_window\"]\n",
    "                )\n",
    "\n",
    "            # #If you really want to overfit\n",
    "            # for i in range(1, config[\"context_window\"] + 1):\n",
    "            #     x = xs[:i]\n",
    "            #     y = ys[:i]\n",
    "            #     logits, loss = model(x, targets=y)\n",
    "            #     loss.backward()\n",
    "            #     optimizer.step()\n",
    "\n",
    "            logits, loss = model(xs, targets=ys)\n",
    "            loss.backward()\n",
    "            optimizer.step()\n",
    "\n",
    "            if scheduler:\n",
    "                scheduler.step()\n",
    "\n",
    "            if epoch % config[\"log_interval\"] == 0:\n",
    "                batch_time = time.time() - start_time\n",
    "                x = get_loss(model, lora=lora)\n",
    "                losses += [x]\n",
    "                if print_logs:\n",
    "                    print(\n",
    "                        f\"Epoch {epoch} | train loss {x['train']:.3f} | val loss {x['val']:.3f} | Time {batch_time:.3f} | ETA: {timedelta(seconds=(batch_time * (config['epochs'] - epoch)/config['log_interval']))}\"\n",
    "                    )\n",
    "                start_time = time.time()\n",
    "\n",
    "                if scheduler:\n",
    "                    print(\"lr: \", scheduler.get_last_lr())\n",
    "        except StopIteration:\n",
    "            print(f\"Reached end of dataset on step {epoch}\")\n",
    "            break\n",
    "\n",
    "    GLOBAL_KEEP_TRACK.append(\n",
    "        f\"{type(model).__name__} {sum([m.numel() for m in model.parameters()])} Params | Train: {losses[-1]['train']:.3f} | Val: {losses[-1]['val']:.3f}\"\n",
    "    )\n",
    "    print(\n",
    "        f\"training loss {losses[-1]['train']:.3f} | validation loss: {losses[-1]['val']:.3f}\"\n",
    "    )\n",
    "    return pd.DataFrame(losses).plot(xlabel=\"Step // 50\", ylabel=\"Loss\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "optimizer = torch.optim.AdamW(\n",
    "    model.parameters(),\n",
    ")\n",
    "train(model, optimizer, data=train_data, print_logs=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "generate(model, config=MASTER_CONFIG)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for i in GLOBAL_KEEP_TRACK:\n",
    "    print(i)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### RMS Normalization"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class RMSNormalization(torch.nn.Module):\n",
    "    def __init__(self, dim: int, eps: float = 1e-6):\n",
    "        super().__init__()\n",
    "        self.eps = eps\n",
    "        self.register_parameter(\n",
    "            \"scale\", nn.Parameter(torch.ones(dim))\n",
    "        )\n",
    "\n",
    "    def forward(self, x):\n",
    "        output = x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps)\n",
    "        return output * self.scale[: x.shape[1], :].unsqueeze(0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class SimpleFeedForwardNN_RMS(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        self.config = config\n",
    "\n",
    "        self.embedding = nn.Embedding(\n",
    "            config[\"vocab_size\"], config[\"d_model\"]\n",
    "        )\n",
    "        self.rms = RMSNormalization((config[\"d_model\"], config[\"d_model\"]))\n",
    "        self.linear = nn.Sequential(\n",
    "            nn.Linear(config[\"d_model\"], config[\"d_model\"]),\n",
    "            nn.ReLU(),\n",
    "            nn.Linear(config[\"d_model\"], config[\"vocab_size\"]),\n",
    "        )\n",
    "\n",
    "        print(\n",
    "            f\"model params: {sum([m.numel() for m in self.parameters()])}\"\n",
    "        )\n",
    "\n",
    "    def forward(self, idx, targets=None):\n",
    "        x = self.embedding(idx)\n",
    "        x = self.rms(x)  # rms pre-normalization\n",
    "        logits = self.linear(x)\n",
    "\n",
    "        if targets is not None:\n",
    "            loss = F.cross_entropy(\n",
    "                logits.view(-1, self.config[\"vocab_size\"]),\n",
    "                targets.view(-1),\n",
    "                ignore_index=tokenizer.pad_token_id,\n",
    "                # reduction=\"sum\",\n",
    "            )\n",
    "            return logits, loss\n",
    "\n",
    "        else:\n",
    "            return logits\n",
    "\n",
    "\n",
    "model = SimpleFeedForwardNN_RMS(MASTER_CONFIG).to(device)\n",
    "optimizer = torch.optim.AdamW(model.parameters())\n",
    "train(model, optimizer, data=train_data, print_logs=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "generate(model, config=MASTER_CONFIG)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for i in GLOBAL_KEEP_TRACK:\n",
    "    print(i)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### RoPE scaling"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_rotary_matrix(context_window, embedding_dim):\n",
    "    R = torch.zeros(\n",
    "        (context_window, embedding_dim, embedding_dim), requires_grad=False\n",
    "    )\n",
    "    for position in range(context_window):\n",
    "        for i in range(embedding_dim // 2):\n",
    "            theta = 10000.0 ** (-2.0 * (i - 1) / embedding_dim)\n",
    "            m_theta = position * theta\n",
    "            R[position, 2 * i, 2 * i] = np.cos(m_theta)\n",
    "            R[position, 2 * i, 2 * i + 1] = -np.sin(m_theta)\n",
    "            R[position, 2 * i + 1, 2 * i] = np.sin(m_theta)\n",
    "            R[position, 2 * i + 1, 2 * i + 1] = np.cos(m_theta)\n",
    "    return R"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython.display import clear_output\n",
    "\n",
    "K = 3\n",
    "config = {\n",
    "    \"batch_size\": 10,\n",
    "    \"d_model\": 32,\n",
    "    \"n_heads\": 8,\n",
    "    \"context_window\": K**2,\n",
    "}\n",
    "batch = torch.randn(1, config[\"context_window\"], config[\"d_model\"])\n",
    "R = get_rotary_matrix(config[\"context_window\"], config[\"d_model\"])\n",
    "# clear_output()\n",
    "fig, ax = plt.subplots(K, K, figsize=(K * 3, K * 4))\n",
    "\n",
    "for i in range(K):\n",
    "    for j in range(K):\n",
    "        ax[i, j].imshow(R[i * K + j, :, :].detach().numpy())\n",
    "        ax[i, j].set_title(f\"rotation at {i * K + j}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "config = {\n",
    "    \"batch_size\": 10,\n",
    "    \"d_model\": 512,\n",
    "    \"n_heads\": 8,\n",
    "    \"context_window\": 16,\n",
    "}\n",
    "\n",
    "\n",
    "class RoPEMaskedAttentionHead(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        self.config = config\n",
    "        self.w_q = nn.Linear(\n",
    "            config[\"d_model\"], config[\"d_model\"], bias=False\n",
    "        )\n",
    "        self.w_k = nn.Linear(\n",
    "            config[\"d_model\"], config[\"d_model\"], bias=False\n",
    "        )\n",
    "        self.w_v = nn.Linear(\n",
    "            config[\"d_model\"], config[\"d_model\"], bias=False\n",
    "        )\n",
    "\n",
    "        self.R = get_rotary_matrix(\n",
    "            config[\"context_window\"], config[\"d_model\"]\n",
    "        ).to(device)\n",
    "\n",
    "    @jit(nopython=False)\n",
    "    def get_rotary_matrix(context_window, embedding_dim):\n",
    "        R = torch.zeros(\n",
    "            (context_window, embedding_dim, embedding_dim),\n",
    "            requires_grad=False,\n",
    "        )\n",
    "        for position in range(context_window):\n",
    "            for i in range(embedding_dim // 2):\n",
    "                theta = 10000.0 ** (-2.0 * (i - 1) / embedding_dim)\n",
    "                m_theta = position * theta\n",
    "                R[position, 2 * i, 2 * i] = np.cos(m_theta)\n",
    "                R[position, 2 * i, 2 * i + 1] = -np.sin(m_theta)\n",
    "                R[position, 2 * i + 1, 2 * i] = np.sin(m_theta)\n",
    "                R[position, 2 * i + 1, 2 * i + 1] = np.cos(m_theta)\n",
    "        return R\n",
    "\n",
    "    def forward(self, x, return_attn_weights=False):\n",
    "        b, m, d = x.shape\n",
    "\n",
    "        q = self.w_q(x).to(device)\n",
    "        k = self.w_k(x).to(device)\n",
    "        v = self.w_v(x).to(device)\n",
    "\n",
    "        q_rotated = (\n",
    "            (torch.bmm(q.transpose(0, 1), self.R[:m]))\n",
    "            .transpose(0, 1)\n",
    "            .to(device)\n",
    "        )\n",
    "        k_rotated = (\n",
    "            (torch.bmm(k.transpose(0, 1), self.R[:m]))\n",
    "            .transpose(0, 1)\n",
    "            .to(device)\n",
    "        )\n",
    "\n",
    "        activations = F.scaled_dot_product_attention(\n",
    "            q_rotated, k_rotated, v, dropout_p=0.1, is_causal=True\n",
    "        )\n",
    "\n",
    "        if return_attn_weights:\n",
    "            attn_mask = torch.tril(torch.ones((m, m)), diagonal=0).to(\n",
    "                device\n",
    "            )\n",
    "            attn_weights = (\n",
    "                torch.bmm(q_rotated, k_rotated.transpose(1, 2)) / np.sqrt(d)\n",
    "                + attn_mask\n",
    "            )\n",
    "            attn_weights = F.softmax(attn_weights, dim=-1)\n",
    "            return activations, attn_weights\n",
    "        return activations\n",
    "\n",
    "\n",
    "layer = RoPEMaskedAttentionHead(config)\n",
    "batch = torch.randn(\n",
    "    (config[\"batch_size\"], config[\"context_window\"], config[\"d_model\"])\n",
    ")\n",
    "output, attn_weights = layer(batch, return_attn_weights=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class RoPEMaskedMultiheadAttention(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        self.config = config\n",
    "        self.heads = nn.ModuleList(\n",
    "            [\n",
    "                RoPEMaskedAttentionHead(config).to(device)\n",
    "                for _ in range(config[\"n_heads\"])\n",
    "            ]\n",
    "        )\n",
    "        self.linear = nn.Linear(\n",
    "            config[\"n_heads\"] * config[\"d_model\"], config[\"d_model\"]\n",
    "        ).to(device)\n",
    "        self.dropout = nn.Dropout(0.1)\n",
    "\n",
    "    def forward(self, x):\n",
    "        x = x.to(device)\n",
    "        heads = [h(x) for h in self.heads]\n",
    "        x = torch.cat(heads, dim=-1)\n",
    "        x = self.linear(x)\n",
    "        x = self.dropout(x)\n",
    "        return x\n",
    "\n",
    "\n",
    "layer = RoPEMaskedMultiheadAttention(MASTER_CONFIG)\n",
    "batch = torch.ones(\n",
    "    (\n",
    "        MASTER_CONFIG[\"batch_size\"],\n",
    "        MASTER_CONFIG[\"context_window\"],\n",
    "        MASTER_CONFIG[\"d_model\"],\n",
    "    )\n",
    ")\n",
    "output = layer(batch)\n",
    "output.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 550 | train loss 4.033 | val loss 4.156 | Time 1.628 | ETA: 0:00:14.651062\n",
      "Epoch 600 | train loss 4.201 | val loss 4.256 | Time 1.618 | ETA: 0:00:12.944433\n",
      "Epoch 650 | train loss 4.230 | val loss 4.344 | Time 1.612 | ETA: 0:00:11.284075\n",
      "Epoch 700 | train loss 4.546 | val loss 4.112 | Time 1.603 | ETA: 0:00:09.618363\n",
      "Epoch 750 | train loss 4.473 | val loss 4.514 | Time 1.611 | ETA: 0:00:08.057482\n",
      "Epoch 800 | train loss 4.590 | val loss 4.129 | Time 1.786 | ETA: 0:00:07.143560\n",
      "Epoch 850 | train loss 4.447 | val loss 4.660 | Time 1.646 | ETA: 0:00:04.936961\n",
      "Epoch 900 | train loss 4.340 | val loss 4.492 | Time 1.635 | ETA: 0:00:03.270883\n",
      "Epoch 950 | train loss 4.122 | val loss 4.216 | Time 1.662 | ETA: 0:00:01.661775\n",
      "training loss 4.122 | validation loss: 4.216\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Axes: xlabel='Step // 50', ylabel='Loss'>"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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8cUX+f7775k/fz5nnXUWANnZ2SQmJrq1wJYoKSaS/WgWYBEREXdoUph55JFHeOmllzjttNOYNGkS/fr1A2DevHmu00/u4HA4mD59Oh07diQiIoLOnTtz//33YxiG2x7DF2rONaMwIyIi0hwhTbnRaaedxv79+ykoKCA+Pt61/5prriEyMtJtxT3yyCPMnDmTN998k169erFixQquvPJKbDYbN954o9sex9vMWYDjgd+12KSIiEgzNSnMlJaWYhiGK8js2LGDOXPm0LNnT0aPHu224n788UfGjx/P2LFjAejQoQOzZ89m2bJlbnsMX0iOsWqxSRERETdp0mmm8ePH89ZbbwGQl5fHkCFDeOKJJzjvvPOYOXOm24obNmwYCxcuZPPmzQCsWbOGH374gTFjxtR5G7vdTkFBQY2LvzEXm4wzN9QyIyIi0ixNCjMrV67k5JNPBuCjjz4iNTWVHTt28NZbb/HMM8+4rbh//vOfTJw4kR49ehAaGkr//v25+eabmTx5cp23mTFjBjabzXXJzMx0Wz3ucuQ0ExhqmREREWmWJoWZkpISYmJiAPjf//7H+eefT1BQECeeeCI7duxwW3EffPAB7777Lv/5z39YuXIlb775Jo8//jhvvvlmnbeZNm0a+fn5rktWVpbb6nGXxOgw9lV1AHbkq2VGRESkOZrUZ6ZLly7MnTuXCRMm8NVXX3HLLbcAkJubS2xsrNuKu/32212tMwB9+vRhx44dzJgxgyuuuKLW21itVtccOP7KGhJMcVgSAIZWzhYREWmWJrXM3HXXXdx222106NCBwYMHM3ToUMBspenfv7/biispKSEoqGaJwcHBOJ1Otz2GrxjR5pIGwcW5EOBDzUVERHypSS0zF154IcOHDycnJ8c1xwzAiBEjmDBhgtuKGzduHA8++CDt2rWjV69erFq1iieffJKrrrrKbY/hK8ExaVAEQc5yKMuDiPhj3kZERESO1qQwA5CWlkZaWppr9ey2bdu6dcI8gGeffZbp06dz/fXXk5ubS0ZGBtdeey133XWXWx/HF2yxMeRlRxFnKYbCvQozIiIiTdSk00xOp5P77rsPm81G+/btad++PXFxcdx///1uPQUUExPDU089xY4dOygtLWXbtm088MADhIWFue0xfCU5uvoswOo3IyIi0lRNapn517/+xWuvvcbDDz/MSSedBMAP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yMggKKj+XjEtvmUmKCiItm3bevQxYmNjW/R/qOr0XFuu1vR89Vxbrtb0fFvDcz1Wi8xh6gAsIiIiAU1hRkRERAKawkwzWK1W7r77bqxWq69L8Tg915arNT1fPdeWqzU939b0XBuqxXcAFhERkZZNLTMiIiIS0BRmREREJKApzIiIiEhAU5gRERGRgKYwcwzPP/88HTp0IDw8nCFDhrBs2bJ6j//www/p0aMH4eHh9OnTh88//9xLlTbdjBkzGDRoEDExMaSkpHDeeeexadOmem/zxhtvYLFYalzCw8O9VHHT3XPPPUfV3aNHj3pvE4iv6WEdOnQ46vlaLBamTp1a6/GB9Lp+9913jBs3joyMDCwWC3Pnzq1xvWEY3HXXXaSnpxMREcHIkSPZsmXLMe+3se95b6jvuVZUVHDHHXfQp08foqKiyMjI4PLLLyc7O7ve+2zKe8FbjvXaTpky5ajazzrrrGPeb6C9tkCt71+LxcJjjz1W533682vrKQoz9Xj//fe59dZbufvuu1m5ciX9+vVj9OjR5Obm1nr8jz/+yKRJk7j66qtZtWoV5513Hueddx7r1q3zcuWN8+233zJ16lSWLl3K/Pnzqaio4Mwzz6S4uLje28XGxpKTk+O67Nixw0sVN0+vXr1q1P3DDz/UeWygvqaHLV++vMZznT9/PgAXXXRRnbcJlNe1uLiYfv368fzzz9d6/aOPPsozzzzDiy++yE8//URUVBSjR4+mrKyszvts7HveW+p7riUlJaxcuZLp06ezcuVKPv74YzZt2sS55557zPttzHvBm4712gKcddZZNWqfPXt2vfcZiK8tUOM55uTk8Prrr2OxWLjgggvqvV9/fW09xpA6DR482Jg6dapr2+FwGBkZGcaMGTNqPf7iiy82xo4dW2PfkCFDjGuvvdajdbpbbm6uARjffvttncfMmjXLsNls3ivKTe6++26jX79+DT6+pbymh910001G586dDafTWev1gfq6AsacOXNc206n00hLSzMee+wx1768vDzDarUas2fPrvN+Gvue94U/PtfaLFu2zACMHTt21HlMY98LvlLb873iiiuM8ePHN+p+WsprO378eOOMM86o95hAeW3dSS0zdSgvL+fnn39m5MiRrn1BQUGMHDmSJUuW1HqbJUuW1DgeYPTo0XUe76/y8/MBSEhIqPe4oqIi2rdv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44UXXjDOOussIzw83OjYsaPx4YcfHrOu7777zkhPTzecTqdr31VXXVXrquHV6/z3v/9d5/UvvPCCER8fX2MV6jvuuMPo3r37MesREc/TaSYRaZLo6Giio6OZO3cudru91mOWL18OwKxZs8jJyXFtf//991x++eXcdNNN/Prrr7z00ku88cYbPPjggzVuP336dC644ALWrFnD5MmTmThxIhs2bKi3rnnz5jFu3DgsFgsATqeTTz/9lPHjx9d7u4cffpjExET69+/PY489RmVlpeu6JUuWcMoppxAWFubaN3r0aDZt2sShQ4fqvV8R8QJfpykRCVwfffSRER8fb4SHhxvDhg0zpk2bZqxZs6bGMYAxZ86cGvtGjBhhPPTQQzX2vf3220Z6enqN2/31r3+tccyQIUOM6667rt6aunbtanz66aeu7cWLFxspKSmGw+Go8zZPPPGEsWjRImPNmjXGzJkzjbi4OOOWW25xXT9q1CjjmmuuqXGb9evXG4Dx66+/1luPiHie+syISJNdcMEFjB07lu+//56lS5fyxRdf8Oijj/Lqq68yZcqUOm+3Zs0aFi9eXKMlxuFwUFZWRklJCZGRkQAMHTq0xu2GDh3K6tWr67zfDRs2kJ2dzYgRI1z7PvnkE8455xyCgupuiL711ltdP/ft25ewsDCuvfZaZsyYgdVqrfN2IuIfdJpJRJolPDycUaNGMX36dH788UemTJnC3XffXe9tioqKuPfee1m9erXrsnbtWrZs2UJ4eHiTa5k3bx6jRo2qcR/z5s3j3HPPbdT9DBkyhMrKSn7//XcA0tLS2Lt3b41jDm+npaU1uV4RcQ+FGRFxq+OOO47i4mLXdmhoKA6Ho8YxAwYMYNOmTXTp0uWoS/UWlKVLl9a43dKlS+nZs2edj/3JJ5/U6BuzZcsWduzYwahRoxr1HFavXk1QUBApKSmA2SL03XffUVFR4Tpm/vz5dO/enfj4+Ebdt4i4n04ziUiTHDhwgIsuuoirrrqKvn37EhMTw4oVK3j00UdrBIoOHTqwcOFCTjrpJKxWK/Hx8dx1112cc845tGvXjgsvvJCgoCDWrFnDunXreOCBB1y3/fDDDxk4cCDDhw/n3XffZdmyZbz22mu11pObm8uKFSuYN2+ea98nn3zCyJEjXaetarNkyRJ++uknTj/9dGJiYliyZAm33HILl112mSuoXHrppdx7771cffXV3HHHHaxbt46nn36af//73839NYqIO/i6046IBKaysjLjn//8pzFgwADDZrMZkZGRRvfu3Y0777zTKCkpcR03b948o0uXLkZISEiNodlffvmlMWzYMCMiIsKIjY01Bg8ebLz88suu6wHj+eefN0aNGmVYrVajQ4cOxvvvv19nPa+++qpx0kkn1dg3fPhw45VXXqn3efz888/GkCFDDJvNZoSHhxs9e/Y0HnroIaOsrKzGcWvWrDGGDx9uWK1Wo02bNsbDDz/ckF+TiHiBxTAMw9eBSkTkjywWC3PmzOG8885r0PHnnnsuw4cP5x//+AcA+/fvJz09nV27dpGamurBSkXE19RnRkRahOHDhzNp0iTX9sGDB3nyyScVZERaAbXMiIhfamzLjIi0XuoALCJ+Sd+zRKShdJpJREREAprCjIiIiAQ0hRkREREJaAozIiIiEtAUZkRERCSgKcyIiIhIQFOYERERkYCmMCMiIiIB7f8DDDp0r0ap0csAAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "class SimpleFeedForwardNN_RMS_Rope(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        self.config = config\n",
    "\n",
    "        self.embedding = nn.Embedding(\n",
    "            config[\"vocab_size\"], config[\"d_model\"]\n",
    "        )\n",
    "        self.rms = RMSNormalization(\n",
    "            (config[\"d_model\"], config[\"d_model\"])\n",
    "        ).to(device)\n",
    "        self.rope_attention = RoPEMaskedMultiheadAttention(config).to(\n",
    "            device\n",
    "        )\n",
    "\n",
    "        self.linear = nn.Sequential(\n",
    "            nn.Linear(config[\"d_model\"], config[\"d_model\"]), nn.ReLU()\n",
    "        )\n",
    "\n",
    "        self.last_linear = nn.Linear(\n",
    "            config[\"d_model\"], config[\"vocab_size\"]\n",
    "        )\n",
    "\n",
    "        print(\n",
    "            f\"model params: {sum([m.numel() for m in self.parameters()])}\"\n",
    "        )\n",
    "\n",
    "    def forward(self, idx, targets=None):\n",
    "        x = self.embedding(idx)\n",
    "\n",
    "        x = self.rms(x)\n",
    "        x = x + self.rope_attention(x)\n",
    "\n",
    "        x = self.rms(x)\n",
    "        x = x + self.linear(x)\n",
    "\n",
    "        logits = self.last_linear(x)\n",
    "\n",
    "        if targets is not None:\n",
    "            loss = F.cross_entropy(\n",
    "                logits.view(-1, self.config[\"vocab_size\"]),\n",
    "                targets.view(-1),\n",
    "                ignore_index=tokenizer.pad_token_id,\n",
    "                # reduction=\"sum\",\n",
    "            )\n",
    "            return logits, loss\n",
    "\n",
    "        else:\n",
    "            return logits\n",
    "\n",
    "\n",
    "model = SimpleFeedForwardNN_RMS_Rope(MASTER_CONFIG).to(device)\n",
    "optimizer = torch.optim.AdamW(model.parameters())\n",
    "train(model, optimizer, data=train_data, print_logs=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['<|begin_of_text|>Write a short story. Possible Story: 3 him. She was holding the bear she could bounce around theulu\\'s go to his things, and I can talk it couldn and!\" wanted to',\n",
       " \"<|begin_of_text|>Write a short story. Possible Story: 3 years old she didn't today. She felt much better held and her toys. She stopped to do something new toys. One day, Lily was\",\n",
       " '<|begin_of_text|>Write a short story. Possible Story: 3 year old owl comes to be cute had to have a to be scared after to help his next time and my walk. She trust it or her',\n",
       " '<|begin_of_text|>Write a short story. Possible Story:  smiled and said, \"It\\'s everything for special wish!\":, \"What\\'s family in and Joe!\" the little mouse crashed, and smooth.',\n",
       " \"<|begin_of_text|>Write a short story. Possible Story:  Everyday playing, he opened it's play book close like it for. It was a little man who lived in a lock to play but out. He\"]"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "generate(model, config=MASTER_CONFIG)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "SimpleFeedForwardNN 74086944 Params | Train: 4.189 | Val: 4.471\n",
      "SimpleFeedForwardNN_RMS 74169888 Params | Train: 4.260 | Val: 4.492\n",
      "SimpleFeedForwardNN_RMS_Rope 76160832 Params | Train: 4.122 | Val: 4.216\n"
     ]
    }
   ],
   "source": [
    "for i in GLOBAL_KEEP_TRACK:\n",
    "    print(i)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### SwiGLU"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "class SwiGLU(nn.Module):\n",
    "    def __init__(self, size):\n",
    "        super().__init__()\n",
    "        self.config = config\n",
    "        self.linear_gate = nn.Linear(size, size)\n",
    "        self.linear = nn.Linear(size, size)\n",
    "        self.beta = torch.randn(1, requires_grad=True)\n",
    "\n",
    "        self.beta = nn.Parameter(torch.ones(1))\n",
    "        self.register_parameter(\"beta\", self.beta)\n",
    "\n",
    "    def forward(self, x):\n",
    "        swish_gate = self.linear_gate(x) * torch.sigmoid(\n",
    "            self.beta * self.linear_gate(x)\n",
    "        )\n",
    "        out = swish_gate * self.linear(x)\n",
    "        return out"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "model params: 76327297\n",
      "Epoch 0 | train loss 11.859 | val loss 11.882 | Time 0.031 | ETA: 0:00:00.617962\n",
      "Epoch 50 | train loss 5.699 | val loss 6.227 | Time 1.680 | ETA: 0:00:31.921274\n",
      "Epoch 100 | train loss 5.289 | val loss 5.644 | Time 1.674 | ETA: 0:00:30.139287\n",
      "Epoch 150 | train loss 4.962 | val loss 5.546 | Time 1.660 | ETA: 0:00:28.219410\n",
      "Epoch 200 | train loss 4.744 | val loss 5.243 | Time 1.677 | ETA: 0:00:26.839001\n",
      "Epoch 250 | train loss 5.080 | val loss 4.997 | Time 1.690 | ETA: 0:00:25.342544\n",
      "Epoch 300 | train loss 4.470 | val loss 4.632 | Time 1.703 | ETA: 0:00:23.841747\n",
      "Epoch 350 | train loss 4.329 | val loss 4.634 | Time 1.722 | ETA: 0:00:22.391763\n",
      "Epoch 400 | train loss 4.603 | val loss 4.613 | Time 1.698 | ETA: 0:00:20.371745\n",
      "Epoch 450 | train loss 4.599 | val loss 4.752 | Time 1.670 | ETA: 0:00:18.371150\n",
      "Epoch 500 | train loss 4.025 | val loss 4.523 | Time 1.672 | ETA: 0:00:16.721938\n",
      "Epoch 550 | train loss 4.347 | val loss 4.394 | Time 1.688 | ETA: 0:00:15.188408\n",
      "Epoch 600 | train loss 4.110 | val loss 4.497 | Time 1.673 | ETA: 0:00:13.386614\n",
      "Epoch 650 | train loss 4.249 | val loss 4.321 | Time 1.830 | ETA: 0:00:12.810095\n",
      "Epoch 700 | train loss 3.859 | val loss 4.110 | Time 1.687 | ETA: 0:00:10.123535\n",
      "Epoch 750 | train loss 4.623 | val loss 3.871 | Time 1.688 | ETA: 0:00:08.441288\n",
      "Epoch 800 | train loss 4.741 | val loss 4.170 | Time 1.659 | ETA: 0:00:06.634128\n",
      "Epoch 850 | train loss 4.551 | val loss 4.467 | Time 1.652 | ETA: 0:00:04.957026\n",
      "Epoch 900 | train loss 4.606 | val loss 4.134 | Time 1.653 | ETA: 0:00:03.306446\n",
      "Epoch 950 | train loss 4.174 | val loss 4.575 | Time 1.650 | ETA: 0:00:01.650354\n",
      "training loss 4.174 | validation loss: 4.575\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Axes: xlabel='Step // 50', ylabel='Loss'>"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjMAAAGwCAYAAABcnuQpAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy80BEi2AAAACXBIWXMAAA9hAAAPYQGoP6dpAABgPklEQVR4nO3dd3hUZfrG8e+kTXrvEEJv0kWKoqKAioBYEcWCDXVxbeuu8rPrKra1u6hr711kwbKAKIr03qQJIUBIICG9z5zfHycZEiEhZVrC/bmuucycOTN5hiHm5j3v+7wWwzAMRERERFooH08XICIiItIcCjMiIiLSoinMiIiISIumMCMiIiItmsKMiIiItGgKMyIiItKiKcyIiIhIi+bn6QJczW63s2/fPsLCwrBYLJ4uR0RERBrAMAwKCgpITk7Gx6f+sZdWH2b27dtHSkqKp8sQERGRJkhPT6dt27b1ntPqw0xYWBhg/mGEh4d7uBoRERFpiPz8fFJSUhy/x+vT6sNM9aWl8PBwhRkREZEWpiFTRDQBWERERFo0hRkRERFp0RRmREREpEVr9XNmREREXMVut1NeXu7pMlokf39/fH19nfJaCjMiIiJNUF5ezs6dO7Hb7Z4upcWKjIwkMTGx2X3gPBpmFi5cyNNPP83KlSvJyMjg66+/5vzzzwegoqKC++67j2+//ZY//viDiIgIRo4cyRNPPEFycrInyxYRkeOcYRhkZGTg6+tLSkrKMZu6SW2GYVBcXExWVhYASUlJzXo9j4aZoqIi+vbty7XXXsuFF15Y67Hi4mJWrVrF/fffT9++fTl06BC33XYb5513HitWrPBQxSIiIlBZWUlxcTHJyckEBwd7upwWKSgoCICsrCzi4+ObdcnJo2Fm9OjRjB49+qiPRUREMHfu3FrHXn75ZQYNGsTu3btp166dO0oUERE5gs1mAyAgIMDDlbRs1UGwoqKi5YaZxsrLy8NisRAZGVnnOWVlZZSVlTnu5+fnu6EyERE5HmnPv+Zx1p9fi7nIV1payt13381ll11Wbyff6dOnExER4bhpXyYREZHWrUWEmYqKCiZMmIBhGMyYMaPec6dNm0ZeXp7jlp6e7qYqRURExBO8PsxUB5m0tDTmzp17zP2VrFarYx8m7cckIiLiGu3bt+f555/3dBmAl8+ZqQ4y27ZtY8GCBcTExHi6pMMqy6FwP/j4Q3jzlpSJiIi4w/Dhw+nXr59TQsjy5csJCQlpflFO4NEwU1hYyPbt2x33d+7cyZo1a4iOjiYpKYmLL76YVatWMXv2bGw2G/v37wcgOjra4zPIy+Y/jnXxcxT0uZawC5/zaC0iIiLOYBgGNpsNP79jx4O4uDg3VNQwHr3MtGLFCvr370///v0BuPPOO+nfvz8PPPAAe/fuZdasWezZs4d+/fqRlJTkuP3222+eLBuAX/ebH/TutB0erkRERDzNMAyKyys9cjMMo0E1Tp48mZ9//pkXXngBi8WCxWLhnXfewWKx8N1333HiiSditVr59ddf2bFjB+PHjychIYHQ0FBOOukk5s2bV+v1/nyZyWKx8MYbb3DBBRcQHBxMly5dmDVrljP/mOvk0ZGZ4cOH1/shNPQD8gSfCPPSUmDpAQ9XIiIinlZSYaPnAz945HtveuRsggOO/ev8hRdeYOvWrfTq1YtHHnkEgI0bNwJwzz338Mwzz9CxY0eioqJIT0/n3HPP5bHHHsNqtfLee+8xbtw4tmzZUm+ft4cffpinnnqKp59+mpdeeolJkyaRlpZGdHS0c95sHbx+ArC3Cow0t1QIqzjo4UpERESOLSIigoCAAIKDg0lMTCQxMdHRqO6RRx5h1KhRdOrUiejoaPr27cuNN95Ir1696NKlC48++iidOnU65kjL5MmTueyyy+jcuTOPP/44hYWFLFu2zOXvzasnAHuz4Ni2AETac8AwQI2TRESOW0H+vmx65GyPfe/mGjhwYK37hYWFPPTQQ8yZM4eMjAwqKyspKSlh9+7d9b5Onz59HF+HhIQQHh7u2H/JlRRmmigizgwzAVRCySEIdu0QmoiIeC+LxdKgSz3e6s+rku666y7mzp3LM888Q+fOnQkKCuLiiy+mvLy83tfx9/evdd9isbhlV/GW+yfvYbGRYWQbYcRYCijO3kOwwoyIiHi5gIAAx75S9Vm0aBGTJ0/mggsuAMyRml27drm4uqbTnJkmCgnw5QBRABQcUJdhERHxfu3bt2fp0qXs2rWLgwcP1jlq0qVLF7766ivWrFnD2rVrufzyy90ywtJUCjNNZLFYyPU1m/iV5Oz1cDUiIiLHdtddd+Hr60vPnj2Ji4urcw7Ms88+S1RUFCeffDLjxo3j7LPPZsCAAW6utuF0makZigJioRTKDynMiIiI9+vatSuLFy+udWzy5MlHnNe+fXt+/PHHWsemTp1a6/6fLzsdrZ1Kbm5uk+psLI3MNENZYDwA9vwMD1ciIiJy/FKYaYbKkAQAfItdv+xMREREjk5hphksYYkAWEsUZkRERDxFYaYZ/Kq6AIeUa0sDERERT1GYaYbgGLNxXnhlVRdgERERcTuFmWYIizFHZvyphOIcD1cjIiJyfFKYaYa4yDAOGuEAGAX7PFyNiIjI8UlhphliQ61kGWYX4JIchRkRERFPUJhphqAAX7ItZpgpPLjHw9WIiIi4Vvv27Xn++ec9XcYRFGaaKd/f3NKgTF2ARUREPEJhpplKrGYXYFueugCLiIh4gsJMM1UGm2HGUrjfw5WIiIjU7fXXXyc5OfmI3a/Hjx/Ptddey44dOxg/fjwJCQmEhoZy0kknMW/ePA9V2zgKM81kDzW7APtrSwMRkeOXYUB5kWduDexzdskll5Cdnc2CBQscx3Jycvj++++ZNGkShYWFnHvuucyfP5/Vq1dzzjnnMG7cuDp31vYm2jW7mfzCkwAIKlMXYBGR41ZFMTye7Jnv/X/7ICDkmKdFRUUxevRoPvroI0aMGAHAF198QWxsLGeccQY+Pj707dvXcf6jjz7K119/zaxZs7jllltcVr4zaGSmmazRbQAIq8hWF2AREfFqkyZN4ssvv6SsrAyADz/8kIkTJ+Lj40NhYSF33XUXPXr0IDIyktDQUDZv3qyRmeNBaFUXYL/qLsAhMR6uSERE3M4/2Bwh8dT3bqBx48ZhGAZz5szhpJNO4pdffuG5554D4K677mLu3Lk888wzdO7cmaCgIC6++GLKy8tdVbnTKMw0U0xEKAeNcGIt+VCQoTAjInI8slgadKnH0wIDA7nwwgv58MMP2b59O926dWPAgAEALFq0iMmTJ3PBBRcAUFhYyK5duzxYbcMpzDRTXJjZBTjWko9RkIElsZenSxIREanTpEmTGDt2LBs3buSKK65wHO/SpQtfffUV48aNw2KxcP/99x+x8slbac5MM8WEBJBpRALa0kBERLzfmWeeSXR0NFu2bOHyyy93HH/22WeJiori5JNPZty4cZx99tmOURtvp5GZZgr09+WQbzQAJdl7aPiVSxEREffz8fFh374j//Hdvn17fvzxx1rHpk6dWuu+t1520siMExQFxAFQkauRGREREXdTmHGCskCzC7BRoC7AIiIi7qYw4wT20AQAfIsyPVyJiIjI8UdhxgksYWYX4MBSbWkgIiLibh4NMwsXLmTcuHEkJydjsViYOXNmrce/+uorzjrrLGJiYrBYLKxZs8YjdR6Lf5TZOC+4IhtayDI2ERFpPkOd35vFWX9+Hg0zRUVF9O3bl1deeaXOx4cNG8aTTz7p5soaJyQqCbthwc+ohJIcT5cjIiIu5uvrC9AiuuN6s+LiYgD8/f2b9ToeXZo9evRoRo8eXefjV155JeC9S8GqxUaEkk0YcVR3AY71dEkiIuJCfn5+BAcHc+DAAfz9/fHx0ayNxjAMg+LiYrKysoiMjHSEw6ZqdX1mysrKHBtoAeTn57v8e8aGml2A4yz5UJAJib1d/j1FRMRzLBYLSUlJ7Ny5k7S0NE+X02JFRkaSmJjY7NdpdWFm+vTpPPzww279nnFhVjYbkZxAGvb8fZpVLSJyHAgICKBLly661NRE/v7+zR6Rqdbqwsy0adO48847Hffz8/NJSUlx6feMCQ0g04gCoPTQXnUBFhE5Tvj4+BAYGOjpMo57rS7MWK1WrFarW7+nv68P+X7mbtllhzIUZkRERNxIV0ScpDTQ3NLAnqctDURERNzJoyMzhYWFbN++3XF/586drFmzhujoaNq1a0dOTg67d+92bIi1ZcsWABITE50yYciZKoIToBQshdrSQERExJ08OjKzYsUK+vfvT//+/QG488476d+/Pw888AAAs2bNon///owZMwaAiRMn0r9/f1599VWP1Vynqi7AASUHPFyIiIjI8cWjIzPDhw+vt/vf5MmTmTx5svsKagbfCDPMBJUfMLsAq+eAiIiIW+g3rpMERyViNyz4GjYozvZ0OSIiIscNhRkniQ43uwADoHkzIiIibqMw4yRxYWYXYAAKFGZERETcRWHGSWJrNM5TmBEREXEfhRknMUdmIgGw52d4thgREZHjiMKMk0QHB5CFOTJTdmivh6sRERE5fijMOImfrw9FAbEAVORpZEZERMRdFGacqCwo3vxCc2ZERETcRmHGiewh5hYLfkWZHq5ERETk+KEw40S+4WYXYGvZQbMLsIiIiLicwowTBTi6AFeqC7CIiIibKMw4UWxYCNmEm3cKNAlYRETEHRRmnCg2rEbjvELNmxEREXEHhRknigsNdDTO08iMiIiIeyjMOFGtkRktzxYREXELhRknigu1OroA27SlgYiIiFsozDhRVHAAB4gEoCJ3n2eLEREROU4ozDiRj4+FEqvZBVibTYqIiLiHwoyTVYYkAOCj1UwiIiJuoTDjZJZQc0uDgNID6gIsIiLiBgozThYQmYDdsOBj2KD4oKfLERERafUUZpwsOiyEg0SYd7Q8W0RExOUUZpwsLsxao3GewoyIiIirKcw4WWxozcZ5WtEkIiLiagozTlZrZEYrmkRERFxOYcbJanYB1siMiIiI6ynMOFlcmNVxmUlbGoiIiLiewoyTRQT5k22JBMCWpzAjIiLiagozTmaxWCgPMrsAazWTiIiI6ynMuIBR1QXYr0RdgEVERFxNYcYF/MLVBVhERMRdPBpmFi5cyLhx40hOTsZisTBz5sxajxuGwQMPPEBSUhJBQUGMHDmSbdu2eabYRogJC67RBVjzZkRERFzJo2GmqKiIvn378sorrxz18aeeeooXX3yRV199laVLlxISEsLZZ59NaWmpmyttnNiwADIdXYDVa0ZERMSV/Dz5zUePHs3o0aOP+phhGDz//PPcd999jB8/HoD33nuPhIQEZs6cycSJE91ZaqPEhVrJMqKAXRqZERERcTGvnTOzc+dO9u/fz8iRIx3HIiIiGDx4MIsXL67zeWVlZeTn59e6uVtsmLXGyIxWNImIiLiS14aZ/fvNEJCQkFDreEJCguOxo5k+fToRERGOW0pKikvrPJpaXYALFWZERERcyWvDTFNNmzaNvLw8xy09Pd3tNcSGVV9mQiMzIiIiLua1YSYx0ezVkplZewJtZmam47GjsVqthIeH17q5W1yNy0x2bWkgIiLiUl4bZjp06EBiYiLz5893HMvPz2fp0qUMHTrUg5UdW5jVj0M+MQDY8zUyIyIi4koeXc1UWFjI9u3bHfd37tzJmjVriI6Opl27dtx+++3885//pEuXLnTo0IH777+f5ORkzj//fM8V3QAWiwVbSAKUgW9xFtht4OPr6bJERERaJY+GmRUrVnDGGWc47t95550AXH311bzzzjv84x//oKioiClTppCbm8uwYcP4/vvvCQwM9FTJDeYbFo+t1IIvNig6CGEJx36SiIiINJrFMAzD00W4Un5+PhEREeTl5bl1/sz1767gsT8uIsGSCzcuhKS+bvveIiIiLV1jfn977ZyZli4uzEqWes2IiIi4nMKMi8SFBpDpWJ6tFU0iIiKuojDjInG1es1ofyYRERFXUZhxkdhQK1lEmnc0MiMiIuIyCjMuYjbOUxdgERERV1OYcZHY0BoTgLU/k4iIiMsozLhIzZEZbWkgIiLiOgozLhJi9SPfPxYAS1FVF2ARERFxOoUZF/IJicNmWLAYdrMLsIiIiDidwowLxYQHc5AI845WNImIiLiEwowLxdZqnKdJwCIiIq6gMONCtbY00IomERERl1CYcSFzebZGZkRERFxJYcaFajfO05wZERERV1CYcaHaWxpofyYRERFXUJhxIY3MiIiIuJ7CjAvFhR4OM0ahRmZERERcQWHGhWqvZspUF2AREREXUJhxoUB/X8qtMTW6AB/wdEkiIiKtjsKMi0WHBXHAMQlYy7NFREScTWHGxeJCa1xqUpgRERFxOoUZF4sNC9CKJhERERdSmHGxuJpdgLWiSURExOkUZlwsttZlJo3MiIiIOJvCjIvFhVnJRPsziYiIuIrCjIvFhtbsAqwwIyIi4mwKMy5Wq3GewoyIiIjTKcy4mBlmqrY0KMpSF2AREREnU5hxsZjQALIJVxdgERERF1GYcTGrny9hQdYaXYC1oklERMSZFGbcIDY0QJOARUREXMTrw0xBQQG33347qampBAUFcfLJJ7N8+XJPl9UomgQsIiLiOl4fZq6//nrmzp3L+++/z/r16znrrLMYOXIke/fu9XRpDRZbswuwwoyIiIhTeXWYKSkp4csvv+Spp57itNNOo3Pnzjz00EN07tyZGTNmeLq8BosLs2p/JhERERfx83QB9amsrMRmsxEYGFjreFBQEL/++utRn1NWVkZZWZnjfn5+vktrbIjYUCvp1ROAtT+TiIiIU3n1yExYWBhDhw7l0UcfZd++fdhsNj744AMWL15MRsbRRzimT59ORESE45aSkuLmqo+kkRkRERHX8eowA/D+++9jGAZt2rTBarXy4osvctlll+Hjc/TSp02bRl5enuOWnp7u5oqPFKc5MyIiIi7j1ZeZADp16sTPP/9MUVER+fn5JCUlcemll9KxY8ejnm+1WrFarW6usn61VjMVHQBbJfh6/R+9iIhIi+D1IzPVQkJCSEpK4tChQ/zwww+MHz/e0yU1WFyYlWzCqTR8QF2ARUREnMrrhwd++OEHDMOgW7dubN++nb///e90796da665xtOlNVh0SAB2fDhAJEnkmPNm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NFZBQQERERH1nmMxGhJ5WjC73c6+ffsICwvDYrE49bXz8/NJSUkhPT2d8PBwp762t9F7bb2Op/er99p6HU/v93h5r4ZhUFBQQHJyMj4+9c+KafUjMz4+PrRt29al3yM8PLxV/4WqSe+19Tqe3q/ea+t1PL3f4+G9HmtEppomAIuIiEiLpjAjIiIiLZrCTDNYrVYefPBBrFarp0txOb3X1ut4er96r63X8fR+j6f32lCtfgKwiIiItG4amREREZEWTWFGREREWjSFGREREWnRFGZERESkRVOYOYZXXnmF9u3bExgYyODBg1m2bFm953/++ed0796dwMBAevfuzbfffuumSptu+vTpnHTSSYSFhREfH8/555/Pli1b6n3OO++8g8ViqXULDAx0U8VN99BDDx1Rd/fu3et9Tkv8TKu1b9/+iPdrsViYOnXqUc9vSZ/rwoULGTduHMnJyVgsFmbOnFnrccMweOCBB0hKSiIoKIiRI0eybdu2Y75uY3/m3aG+91pRUcHdd99N7969CQkJITk5mauuuop9+/bV+5pN+Vlwl2N9tpMnTz6i9nPOOeeYr9vSPlvgqD+/FouFp59+us7X9ObP1lUUZurx6aefcuedd/Lggw+yatUq+vbty9lnn01WVtZRz//tt9+47LLLuO6661i9ejXnn38+559/Phs2bHBz5Y3z888/M3XqVJYsWcLcuXOpqKjgrLPOoqioqN7nhYeHk5GR4bilpaW5qeLmOeGEE2rV/euvv9Z5bkv9TKstX7681nudO3cuAJdcckmdz2kpn2tRURF9+/bllVdeOerjTz31FC+++CKvvvoqS5cuJSQkhLPPPpvS0tI6X7OxP/PuUt97LS4uZtWqVdx///2sWrWKr776ii1btnDeeecd83Ub87PgTsf6bAHOOeecWrV//PHH9b5mS/xsgVrvMSMjg7feeguLxcJFF11U7+t662frMobUadCgQcbUqVMd9202m5GcnGxMnz79qOdPmDDBGDNmTK1jgwcPNm688UaX1ulsWVlZBmD8/PPPdZ7z9ttvGxEREe4rykkefPBBo2/fvg0+v7V8ptVuu+02o1OnTobdbj/q4y31cwWMr7/+2nHfbrcbiYmJxtNPP+04lpuba1itVuPjjz+u83Ua+zPvCX9+r0ezbNkyAzDS0tLqPKexPwuecrT3e/XVVxvjx49v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1VdZv3491157LZGRkUyZMsU1b0REGs7Tm0OJSMt06NAhAzB++umnOs9JTU2ttXleamqq47GZM2ca/fv3N6xWq9GhQwfjoYceMioqKhyPA8a///1v45xzzjECAwONDh06GJ9//vkx61q4cKGRlJRk2O12x7Frr732qLuG16zzueeeq/Pxf//730ZUVFStXajvvvtuo1u3bsesR0RcT5eZRKRJQkNDCQ0NZebMmZSVlR31nOXLlwPw9ttvk5GR4bj/yy+/cNVVV3HbbbexadMmXnvtNd555x0ee+yxWs+///77ueiii1i7di2TJk1i4sSJbN68ud66Zs2axbhx47BYLADY7XZmz57N+PHj633eE088QUxMDP379+fpp5+msrLS8djixYs57bTTCAgIcBw7++yz2bJlC4cOHar3dUXEDTydpkSk5friiy+MqKgoIzAw0Dj55JONadOmGWvXrq11DmB8/fXXtY6NGDHCePzxx2sde//9942kpKRaz7vppptqnTN48GDj5ptvrremLl26GLNnz3bcX7RokREfH2/YbLY6n/Ovf/3LWLBggbF27VpjxowZRmRkpHHHHXc4Hh81apQxZcqUWs/ZuHGjARibNm2qtx4RcT3NmRGRJrvooosYM2YMv/zyC0uWLOG7777jqaee4o033mDy5Ml1Pm/t2rUsWrSo1kiMzWajtLSU4uJigoODARg6dGit5w0dOpQ1a9bU+bqbN29m3759jBgxwnHsm2++YezYsfj41D0Qfeeddzq+7tOnDwEBAdx4441Mnz4dq9Va5/NExDvoMpOINEtgYCCjRo3i/vvv57fffmPy5Mk8+OCD9T6nsLCQhx9+mDVr1jhu69evZ9u2bQQGBja5llmzZjFq1KharzFr1izOO++8Rr3O4MGDqaysZNeuXQAkJiaSmZlZ65zq+4mJiU2uV0ScQ2FGRJyqZ8+eFBUVOe77+/tjs9lqnTNgwAC2bNlC586dj7jVHEFZsmRJrectWbKEHj161Pm9v/nmm1pzY7Zt20ZaWhqjRo1q1HtYs2YNPj4+xMfHA+aI0MKFC6moqHCcM3fuXLp160ZUVFSjXltEnE+XmUSkSbKzs7nkkku49tpr6dOnD2FhYaxYsYKnnnqqVqBo37498+fP55RTTsFqtRIVFcUDDzzA2LFjadeuHRdffDE+Pj6sXbuWDRs28M9//tPx3M8//5yBAwcybNgwPvzwQ5YtW8abb7551HqysrJYsWIFs2bNchz75ptvGDlypOOy1dEsXryYpUuXcsYZZxAWFsbixYu54447uOKKKxxB5fLLL+fhhx/muuuu4+6772bDhg288MILPPfcc839YxQRZ/D0pB0RaZlKS0uNe+65xxgwYIARERFhBAcHG926dTPuu+8+o7i42HHerFmzjM6dOxt+fn61lmZ///33xsknn2wEBQUZ4eHhxqBBg4zXX3/d8ThgvPLKK8aoUaMMq9VqtG/f3vj000/rrOeNN94wTjnllFrHhg0bZvznP/+p932sXLnSGDx4sBEREWEEBgYaPXr0MB5//HGjtLS01nlr1641hg0bZlitVqNNmzbGE0880ZA/JhFxA4thGIanA5WIyJ9ZLBa+/vprzj///Aadf9555zFs2DD+8Y9/AHDw4EGSkpLYs2cPCQkJLqxURDxNc2ZEpFUYNmwYl112meN+Tk4Ozz77rIKMyHFAIzMi4pUaOzIjIscvTQAWEa+kf2eJSEPpMpOIiIi0aAozIiIi0qIpzIiIiEiLpjAjIiIiLZrCjIiIiLRoCjMiIiLSoinMiIiISIumMCMiIiIt2v8DAVWn/TiQMQoAAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "class SimpleFeedForwardNN_RMS_RoPE_SwiGLU(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        self.config = config\n",
    "\n",
    "        self.embedding = nn.Embedding(\n",
    "            config[\"vocab_size\"], config[\"d_model\"]\n",
    "        )\n",
    "        self.rms = RMSNormalization((config[\"d_model\"], config[\"d_model\"]))\n",
    "        self.rope_attention = RoPEMaskedMultiheadAttention(config)\n",
    "\n",
    "        self.linear = nn.Sequential(\n",
    "            nn.Linear(config[\"d_model\"], config[\"d_model\"]),\n",
    "            SwiGLU(config[\"d_model\"]),\n",
    "        )\n",
    "\n",
    "        self.last_linear = nn.Linear(\n",
    "            config[\"d_model\"], config[\"vocab_size\"]\n",
    "        )\n",
    "\n",
    "        print(\n",
    "            f\"model params: {sum([m.numel() for m in self.parameters()])}\"\n",
    "        )\n",
    "\n",
    "    def forward(self, idx, targets=None):\n",
    "        x = self.embedding(idx)\n",
    "\n",
    "        x = self.rms(x)\n",
    "        x = x + self.rope_attention(x)\n",
    "\n",
    "        x = self.rms(x)\n",
    "        x = x + self.linear(x)\n",
    "\n",
    "        logits = self.last_linear(x)\n",
    "\n",
    "        if targets is not None:\n",
    "            loss = F.cross_entropy(\n",
    "                logits.view(-1, self.config[\"vocab_size\"]),\n",
    "                targets.view(-1),\n",
    "                ignore_index=tokenizer.pad_token_id,\n",
    "                # reduction=\"sum\",\n",
    "            )\n",
    "            return logits, loss\n",
    "\n",
    "        else:\n",
    "            return logits\n",
    "\n",
    "\n",
    "model = SimpleFeedForwardNN_RMS_RoPE_SwiGLU(MASTER_CONFIG).to(device)\n",
    "optimizer = torch.optim.AdamW(model.parameters())\n",
    "train(model, optimizer, data=train_data, print_logs=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['<|begin_of_text|>Write a short story. Possible Story:  stand. Possible Story: One sunny day, the family kept enjoying and Haus and laughed That has her playing with his family to play balancing. That mom',\n",
       " \"<|begin_of_text|>Write a short story. Possible Story:  say the board. They both licked the cro and didn't the ink the girl. She was sad and saw a cool. The boy whispered. She\",\n",
       " '<|begin_of_text|>Write a short story. Possible Story:  Billy was still snacks grabbed McN rabbit very muchmy was too! The girl was so happy and suddenly loved watching happy. He smiled. One day,',\n",
       " '<|begin_of_text|>Write a short story. Possible Story:  Ben too. They saw the ice cream and saw some cookies. They had treasure fell by the village absolutely crying, but it was so excited to try',\n",
       " '<|begin_of_text|>Write a short story. Possible Story:  mommy and Mil \"Yes there.\" She hope or something. Tom wasabel. He crawl, he could show the· of the home.  and']"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "generate(model, config=MASTER_CONFIG)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "SimpleFeedForwardNN 74086944 Params | Train: 4.189 | Val: 4.471\n",
      "SimpleFeedForwardNN_RMS 74169888 Params | Train: 4.260 | Val: 4.492\n",
      "SimpleFeedForwardNN_RMS_Rope 76160832 Params | Train: 4.122 | Val: 4.216\n",
      "SimpleFeedForwardNN_RMS_RoPE_SwiGLU 76327297 Params | Train: 4.174 | Val: 4.575\n"
     ]
    }
   ],
   "source": [
    "for i in GLOBAL_KEEP_TRACK:\n",
    "    print(i)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "71"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "del model\n",
    "with torch.no_grad():\n",
    "    torch.cuda.empty_cache()\n",
    "import gc\n",
    "\n",
    "gc.collect()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Llama"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "class LlamaBlock(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        self.config = config\n",
    "\n",
    "        self.rms = RMSNormalization(\n",
    "            (config[\"d_model\"], config[\"d_model\"])\n",
    "        )\n",
    "\n",
    "        self.attention = RoPEMaskedMultiheadAttention(config).to(device)\n",
    "        self.feedforward = nn.Sequential(\n",
    "            nn.Linear(config[\"d_model\"], config[\"hidden_dim\"]),\n",
    "            SwiGLU(config[\"hidden_dim\"]),\n",
    "            nn.Linear(config[\"hidden_dim\"], config[\"d_model\"]),\n",
    "        )\n",
    "\n",
    "    def forward(self, x):\n",
    "        h = x + self.attention(self.rms(x))\n",
    "        out = h + self.feedforward(self.rms(x))\n",
    "        return out"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [],
   "source": [
    "class SimpleLlama(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        self.config = config\n",
    "\n",
    "        self.embedding = nn.Embedding(\n",
    "            config[\"vocab_size\"], config[\"d_model\"]\n",
    "        )\n",
    "        self.llama_blocks = nn.Sequential(\n",
    "            OrderedDict(\n",
    "                [\n",
    "                    (f\"llama_{i}\", LlamaBlock(config).to(device))\n",
    "                    for i in range(config[\"n_layers\"])\n",
    "                ]\n",
    "            )\n",
    "        )\n",
    "\n",
    "        self.ffn = nn.Sequential(\n",
    "            nn.Linear(config[\"d_model\"], config[\"d_model\"]),\n",
    "            SwiGLU(config[\"d_model\"]),\n",
    "            nn.Linear(config[\"d_model\"], config[\"vocab_size\"]),\n",
    "        )\n",
    "\n",
    "        print(\n",
    "            f\"model params: {sum([m.numel() for m in self.parameters()])}\"\n",
    "        )\n",
    "\n",
    "    def forward(self, idx, targets=None):\n",
    "        x = self.embedding(idx)\n",
    "        x = self.llama_blocks(x)\n",
    "        logits = self.ffn(x)\n",
    "\n",
    "        if targets is None:\n",
    "            return logits\n",
    "\n",
    "        else:\n",
    "            loss = F.cross_entropy(\n",
    "                logits.view(-1, self.config[\"vocab_size\"]),\n",
    "                targets.view(-1),\n",
    "                ignore_index=tokenizer.pad_token_id,\n",
    "                # reduction=\"sum\",\n",
    "            )\n",
    "            return logits, loss"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "model params: 96444391\n",
      "Epoch 0 | train loss 11.728 | val loss 11.725 | Time 0.244 | ETA: 0:00:04.887486\n",
      "Epoch 50 | train loss 6.848 | val loss 6.659 | Time 3.357 | ETA: 0:01:03.783976\n",
      "Epoch 100 | train loss 5.969 | val loss 5.776 | Time 3.369 | ETA: 0:01:00.640025\n",
      "Epoch 150 | train loss 5.351 | val loss 5.842 | Time 3.347 | ETA: 0:00:56.907477\n",
      "Epoch 200 | train loss 5.066 | val loss 5.724 | Time 3.308 | ETA: 0:00:52.926136\n",
      "Epoch 250 | train loss 4.996 | val loss 5.338 | Time 3.333 | ETA: 0:00:50.001283\n",
      "Epoch 300 | train loss 5.972 | val loss 6.369 | Time 3.352 | ETA: 0:00:46.923183\n",
      "Epoch 350 | train loss 11.583 | val loss 11.590 | Time 3.375 | ETA: 0:00:43.878220\n",
      "Epoch 400 | train loss 259916.003 | val loss 291686.789 | Time 3.398 | ETA: 0:00:40.771173\n",
      "Epoch 450 | train loss 11.360 | val loss 11.366 | Time 3.391 | ETA: 0:00:37.302152\n",
      "Epoch 500 | train loss 11.254 | val loss 11.264 | Time 3.356 | ETA: 0:00:33.560960\n",
      "Epoch 550 | train loss 11.166 | val loss 11.164 | Time 3.378 | ETA: 0:00:30.400196\n",
      "Epoch 600 | train loss 11.090 | val loss 11.072 | Time 3.381 | ETA: 0:00:27.047180\n",
      "Epoch 650 | train loss 11.014 | val loss 10.994 | Time 3.368 | ETA: 0:00:23.575438\n",
      "Epoch 700 | train loss 10.911 | val loss 10.913 | Time 3.378 | ETA: 0:00:20.265264\n",
      "Epoch 750 | train loss 10.831 | val loss 10.823 | Time 3.315 | ETA: 0:00:16.574745\n",
      "Epoch 800 | train loss 10.733 | val loss 10.748 | Time 3.317 | ETA: 0:00:13.269234\n",
      "Epoch 850 | train loss 10.668 | val loss 10.681 | Time 3.318 | ETA: 0:00:09.954606\n",
      "Epoch 900 | train loss 10.590 | val loss 10.601 | Time 3.306 | ETA: 0:00:06.611345\n",
      "Epoch 950 | train loss 10.516 | val loss 10.530 | Time 3.464 | ETA: 0:00:03.464074\n",
      "training loss 10.516 | validation loss: 10.530\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Axes: xlabel='Step // 50', ylabel='Loss'>"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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t7vB5/zpT38SW7duFfv37xfLly8XNptNzJ49W3n81ltvFQ8++GDAcw4dOiQAiK+//rrReogotLimioguCxMmTEB2djY+/fRT7N69Gx9//DEWLVqE119/HVOnTm3wefv378fOnTsDZqbcbjeqqqpQUVEBo9EIAMjIyAh4XkZGBoqKiho87jfffIMTJ05gxIgRyn0ffvghbrvtNmg0DZ8kyM3NVb4fMGAA9Ho9HnroISxcuBAGg6HB5xGR+nj6j4guG7Gxsbj11lvx1FNPYdeuXZg6dSrmz5/f6HPKysrwzDPPoKioSPk6cOAAvvvuO8TGxra6lvXr1+PWW28NOMb69eubXE91sfT0dNTW1uLHH38EAKSmpqK0tDRgjHw7NTW11fUSUdsxVBHRZatfv34oLy9Xbut0Orjd7oAxgwYNQnFxMXr16nXJl/+M0u7duwOet3v3bvTt27fB1/7www8D1k599913OHLkCG699dYWvYeioiJoNBokJycD8M6Q7dixAy6XSxmTl5eH3r17IyEhoUXHJqLg4uk/Iop6Z8+exS9/+Uvcd999GDBgAOLj4/HFF19g0aJFAcGme/fuyM/Px4033giDwYCEhATMmzcPt912G7p27Yo777wTGo0G+/fvx8GDB/H73/9eee7atWtx3XXXYdiwYXj77bexd+9evPHGG/XWc+rUKXzxxRdYv369ct+HH36IzMxM5XRifQoKCrBnzx4MHz4c8fHxKCgowOzZs/HrX/9aCUx33303nnnmGUyfPh1z5szBwYMHsXTpUrz44ott/TESUVupvaiLiKitqqqqxBNPPCEGDRokrFarMBqNonfv3uLJJ58UFRUVyrj169eLXr16iZiYGNGtWzfl/k2bNokbbrhBxMXFCYvFIoYOHSpee+015XEAYtmyZeLWW28VBoNBdO/eXaxevbrBel5//XVx4403Btw3bNgw8T//8z+Nvo/CwkKRnp4urFariI2NFX379hXPPfecqKqqChi3f/9+MWzYMGEwGMQVV1whnn/++eb8mIgoxCQhhFA72BERRTJJkrBu3TqMGzeuWeNvv/12DBs2DI8//jgA4MyZM+jUqROOHz+OlJSUEFZKRGrimioioiAbNmwYJk2apNw+d+4clixZwkBFdJnjTBURURNaOlNFRO0TF6oTETWB/+9JRM3B039EREREQcBQRURERBQEDFVEREREQcBQRURERBQEDFVEREREQcBQRURERBQEDFVEREREQcBQRURERBQE/x/w4qBfjsM1ugAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "llama = SimpleLlama(MASTER_CONFIG).to(device)\n",
    "optimizer = torch.optim.AdamW(llama.parameters())\n",
    "train(\n",
    "    llama, optimizer, data=train_data, config=MASTER_CONFIG, print_logs=True\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is why you need a scheduler lol"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['<|begin_of_text|>Write a short story. Possible Story:  The happy happy her: the. her she on, Tim she Lily. that was day \" she, but to it story the on, Lily and, and a Tim happy,. but said said said a she for happy it on that \" and',\n",
       " '<|begin_of_text|>Write a short story. Possible Story:, for to was she the her happy a but., the it but in on was story she The \" it The was she: story day \" she story, the said Lily: The Story:. in in. but her day for was for',\n",
       " '<|begin_of_text|>Write a short story. Possible Story:  \" Story but a it: The Tim the: Lily said. Tim Lily it said, Lily the said in Story said day Story she to on and that said for. that and, on that to it Story. on was Tim she happy that Story',\n",
       " '<|begin_of_text|>Write a short story. Possible Story: : in Lily but a day that a,: and story, Story day day and for on in on. story day Tim Lily was she was the a that a to the and: she, her day happy happy to a day that: to for',\n",
       " '<|begin_of_text|>Write a short story. Possible Story:,. Story Lily to for the and and she happy it.. on to. Story a The it Tim a day a: to on her a said. that happy day to was to happy that: but on The Story Lily it \" her.']"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "generate(\n",
    "    llama,\n",
    "    config=MASTER_CONFIG,\n",
    "    temperature=1.0,\n",
    "    top_k=25,\n",
    "    max_new_tokens=50,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "SimpleFeedForwardNN 74086944 Params | Train: 4.189 | Val: 4.471\n",
      "SimpleFeedForwardNN_RMS 74169888 Params | Train: 4.260 | Val: 4.492\n",
      "SimpleFeedForwardNN_RMS_Rope 76160832 Params | Train: 4.122 | Val: 4.216\n",
      "SimpleFeedForwardNN_RMS_RoPE_SwiGLU 76327297 Params | Train: 4.174 | Val: 4.575\n",
      "SimpleLlama 96444391 Params | Train: 10.516 | Val: 10.530\n"
     ]
    }
   ],
   "source": [
    "for i in GLOBAL_KEEP_TRACK:\n",
    "    print(i)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "model params: 335771145\n"
     ]
    }
   ],
   "source": [
    "#MASTER_CONFIG['epochs'] = 52171228 // MASTER_CONFIG['batch_size'] # If you want to do a full epoch\n",
    "MASTER_CONFIG['epochs'] = 1000\n",
    "MASTER_CONFIG[\"batch_size\"] = 16\n",
    "MASTER_CONFIG[\"d_model\"] = 768\n",
    "MASTER_CONFIG[\"n_layers\"] = 8\n",
    "MASTER_CONFIG[\"context_window\"] = 64\n",
    "\n",
    "llama = SimpleLlama(MASTER_CONFIG).to(device)\n",
    "\n",
    "llama_optimizer = torch.optim.AdamW(\n",
    "    llama.parameters(),\n",
    "    betas=(0.9, 0.95),\n",
    "    weight_decay=1e-1,\n",
    "    eps=1e-9,\n",
    "    lr=5e-4,\n",
    ")\n",
    "scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(\n",
    "    llama_optimizer, 1000, eta_min=1e-5\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 0 | train loss 11.709 | val loss 11.684 | Time 0.313 | ETA: 0:00:06.267281\n",
      "lr:  [0.0004999987909744553]\n",
      "Epoch 50 | train loss 6.900 | val loss 7.298 | Time 16.386 | ETA: 0:05:11.327543\n",
      "lr:  [0.0004968620434287298]\n",
      "Epoch 100 | train loss 5.657 | val loss 5.853 | Time 16.346 | ETA: 0:04:54.230445\n",
      "lr:  [0.00048776984967968684]\n",
      "Epoch 150 | train loss 5.619 | val loss 5.597 | Time 16.363 | ETA: 0:04:38.168279\n",
      "lr:  [0.0004729460897126908]\n",
      "Epoch 200 | train loss 5.305 | val loss 5.427 | Time 16.374 | ETA: 0:04:21.982731\n",
      "lr:  [0.0004527557736953662]\n",
      "Epoch 250 | train loss 5.687 | val loss 6.308 | Time 16.404 | ETA: 0:04:06.059110\n",
      "lr:  [0.0004276960542158767]\n",
      "Epoch 300 | train loss 4.920 | val loss 5.247 | Time 16.341 | ETA: 0:03:48.774972\n",
      "lr:  [0.00039838398473624826]\n",
      "Epoch 350 | train loss 5.209 | val loss 5.015 | Time 16.249 | ETA: 0:03:31.236198\n",
      "lr:  [0.00036554132568825045]\n",
      "Epoch 400 | train loss 4.734 | val loss 5.247 | Time 16.323 | ETA: 0:03:15.880179\n",
      "lr:  [0.0003299767723361813]\n",
      "Epoch 450 | train loss 4.966 | val loss 4.789 | Time 16.362 | ETA: 0:02:59.978693\n",
      "lr:  [0.00029256604201555376]\n",
      "Epoch 500 | train loss 4.655 | val loss 4.762 | Time 16.328 | ETA: 0:02:43.283188\n",
      "lr:  [0.0002542303110659579]\n",
      "Epoch 550 | train loss 4.365 | val loss 4.677 | Time 16.345 | ETA: 0:02:27.107743\n",
      "lr:  [0.00021591353241236954]\n",
      "Epoch 600 | train loss 4.504 | val loss 4.441 | Time 16.308 | ETA: 0:02:10.467733\n",
      "lr:  [0.0001785591923113374]\n",
      "Epoch 650 | train loss 4.350 | val loss 4.626 | Time 16.326 | ETA: 0:01:54.280152\n",
      "lr:  [0.0001430870785880959]\n",
      "Epoch 700 | train loss 4.118 | val loss 4.616 | Time 16.317 | ETA: 0:01:37.901042\n",
      "lr:  [0.00011037063240770733]\n",
      "Epoch 750 | train loss 3.975 | val loss 4.569 | Time 16.330 | ETA: 0:01:21.652101\n",
      "lr:  [8.121544125479255e-05]\n",
      "Epoch 800 | train loss 3.899 | val loss 4.465 | Time 16.396 | ETA: 0:01:05.585905\n",
      "lr:  [5.6339402696068356e-05]\n",
      "Epoch 850 | train loss 3.856 | val loss 4.426 | Time 16.351 | ETA: 0:00:49.052626\n",
      "lr:  [3.6355047359689e-05]\n",
      "Epoch 900 | train loss 3.850 | val loss 4.500 | Time 16.375 | ETA: 0:00:32.749780\n",
      "lr:  [2.1754456398309237e-05]\n",
      "Epoch 950 | train loss 4.178 | val loss 4.442 | Time 16.271 | ETA: 0:00:16.271245\n",
      "lr:  [1.2897144817981292e-05]\n",
      "training loss 4.178 | validation loss: 4.442\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Axes: xlabel='Step // 50', ylabel='Loss'>"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjMAAAGyCAYAAAARVkUiAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy80BEi2AAAACXBIWXMAAA9hAAAPYQGoP6dpAABev0lEQVR4nO3dd3xV9f3H8de9GTf7ZgAZEBL2BkGGIChWBFER90IFcbRWf2qtrbWtu4qjxdFat+C22gpSUSkgyl5CkL0MIZCEEcje957fHye5JEJCxk3uvcn7+XicR+49634Ot2nefs/3fL8WwzAMRERERHyU1dMFiIiIiDSFwoyIiIj4NIUZERER8WkKMyIiIuLTFGZERETEpynMiIiIiE9TmBERERGfpjAjIiIiPk1hRkRERHyav6cLaG5Op5OMjAzCw8OxWCyeLkdERETqwTAM8vPzSUhIwGo9TduL4UHff/+9cckllxjx8fEGYMyZM8e1rayszPj9739v9O/f3wgJCTHi4+ONm266yTh48GCDPiM9Pd0AtGjRokWLFi0+uKSnp5/2b71HW2YKCwsZNGgQ06dP54orrqixraioiA0bNvDwww8zaNAgjh8/zr333sull17K+vXr6/0Z4eHhAKSnpxMREeHW+kVERKR55OXlkZiY6Po7XheLYXjHRJMWi4U5c+Zw2WWX1brPunXrGD58OGlpaXTu3Lle583Ly8Nut5Obm6swIyIi4iMa8vfbp/rM5ObmYrFYiIyMrHWf0tJSSktLXe/z8vJaoDIRERHxFJ95mqmkpIQHH3yQ66+/vs6ENmPGDOx2u2tJTExswSpFRESkpflEmCkvL+eaa67BMAxeffXVOvd96KGHyM3NdS3p6ektVKWIiIh4gtffZqoKMmlpaXz77benvW9ms9mw2WwtVJ2IiLRlTqeTsrIyT5fhkwICAvDz83PLubw6zFQFmd27d7NkyRJiYmI8XZKIiAgAZWVlpKam4nQ6PV2Kz4qMjCQuLq7J48B5NMwUFBSwZ88e1/vU1FRSUlKIjo4mPj6eq666ig0bNvDll1/icDjIysoCIDo6msDAQE+VLSIibZxhGGRmZuLn50diYuLpB3WTGgzDoKioiMOHDwMQHx/fpPN59NHs7777jvPOO++k9VOnTuWxxx6jS5cupzxuyZIljB07tl6foUezRUTE3crLy9mzZw8JCQnY7XZPl+OzsrOzOXz4MD179jzplpPPPJo9duxY6spSXjIEjoiISA0OhwNAdwmaKCQkBDDDYVP6z6hdTEREpJE051/TuOvfT2FGREREfJrCjIiIiDRYcnIyL774oqfLALz80WwRERFxn7Fjx3LGGWe4JYSsW7eO0NDQphflBgozjVTucHIorwR/q5U4e5CnyxEREWkywzBwOBz4+58+HrRv374FKqof3WZqpJkLdzH+2a95d/FGT5ciIiJyWtOmTeP777/npZdewmKxYLFYmD17NhaLha+//pozzzwTm83G8uXL2bt3L5MnTyY2NpawsDCGDRvGokWLapzv57eZLBYLb731FpdffjkhISH06NGDefPmtci1Kcw00sTDb7MtaDpD9r3p6VJERMTDDMOgqKzCI0t9hzF56aWXGDlyJLfffjuZmZlkZma6JmP+wx/+wDPPPMP27dsZOHAgBQUFXHTRRSxevJiNGzdy4YUXMmnSJPbv31/nZzz++ONcc801/Pjjj1x00UVMmTKFY8eONfnf93R0m6mRAiPN0QrDig96uBIREfG04nIHfR9Z4JHP3vbEBEICT//n3G63ExgYSEhICHFxcQDs2LEDgCeeeIILLrjAtW90dDSDBg1yvX/yySeZM2cO8+bN4+677671M6ZNm8b1118PwNNPP83LL7/M2rVrufDCCxt1bfWllplGCunQFYDo8iwPVyIiItI0Q4cOrfG+oKCABx54gD59+hAZGUlYWBjbt28/bcvMwIEDXa9DQ0OJiIhwTVnQnNQy00hRHXsAEG8coqSsgqB6pGIREWmdggP82PbEBI99dlP9/KmkBx54gIULF/LXv/6V7t27ExwczFVXXXXaGcIDAgJqvLdYLC0yEaf+AjdSWGwyABGWYvYdyiI5sZNnCxIREY+xWCz1utXjaYGBga6pGOqyYsUKpk2bxuWXXw6YLTX79u1r5uoaT7eZGskSGMpxizm52PGMvR6uRkRE5PSSk5NZs2YN+/bt4+jRo7W2mvTo0YPPP/+clJQUNm3axA033NAiLSyNpTDTBNkBZifgosMKMyIi4v0eeOAB/Pz86Nu3L+3bt6+1D8zMmTOJiopi1KhRTJo0iQkTJjBkyJAWrrb+vL9NzIsVBidA2Q4qjqV5uhQREZHT6tmzJ6tWraqxbtq0aSftl5yczLfffltj3V13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+rvgTS688ELee+89Fi9ezLPPPsv333/PxIkTcTgcp9y/tXy37777LuHh4VxxxRV17ufL321jtfqJJqVh7rrrLrZs2XLa+6sjR45k5MiRrvejRo2iT58+vP766zz55JPNXWajTZw40fV64MCBjBgxgqSkJD799NN6/deOL3v77beZOHEiCQkJte7jq9+rmMrLy7nmmmswDINXX321zn19+Xfhuuuuc70eMGAAAwcOpFu3bnz33Xecf/75Hqyseb3zzjtMmTLltJ3yffm7bSy1zNSiXbt2+Pn5cejQoRrrDx06RFxc3CmPiYuLa9D+3ubuu+/myy+/ZMmSJQ2eaTwgIIDBgwezZ8+eZqqueURGRtKzZ89a6/b177RKWloaixYt4rbbbmvQcb76vVZ9Pw357hrzO+9NqoJMWloaCxcubPBsyqf7XfBmXbt2pV27drXW7uvfLcCyZcvYuXNng3+Hwbe/2/pSmKlFYGAgZ555JosXL3atczqdLF68uMZ/uVY3cuTIGvsDLFy4sNb9vYVhGNx9993MmTOHb7/9li5dujT4HA6Hg82bNxMfH98MFTafgoIC9u7dW2vdvvqd/tysWbPo0KEDF198cYOO89XvtUuXLsTFxdX47vLy8lizZk2t311jfue9RVWQ2b17N4sWLSImJqbB5zjd74I3O3DgANnZ2bXW7svfbZW3336bM888k0GDBjX4WF/+buvN0z2Qvdknn3xi2Gw2Y/bs2ca2bduMO+64w4iMjDSysrIMwzCMm266yfjDH/7g2n/FihWGv7+/8de//tXYvn278eijjxoBAQHG5s2bPXUJ9XLnnXcadrvd+O6774zMzEzXUlRU5Nrn59f6+OOPGwsWLDD27t1r/PDDD8Z1111nBAUFGVu3bvXEJdTbb3/7W+O7774zUlNTjRUrVhjjxo0z2rVrZxw+fNgwjNbznVbncDiMzp07Gw8++OBJ23z5e83Pzzc2btxobNy40QCMmTNnGhs3bnQ9wfPMM88YkZGRxhdffGH8+OOPxuTJk40uXboYxcXFrnP84he/MP7+97+73p/ud95T6rrWsrIy49JLLzU6depkpKSk1PgdLi0tdZ3j59d6ut8FT6rrevPz840HHnjAWLVqlZGammosWrTIGDJkiNGjRw+jpKTEdY7W8N1Wyc3NNUJCQoxXX331lOfwpe+2uSjMnMbf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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "train(\n",
    "    llama,\n",
    "    llama_optimizer,\n",
    "    scheduler=scheduler,\n",
    "    data=train_data,\n",
    "    config=MASTER_CONFIG,\n",
    "    print_logs=True,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['<|begin_of_text|>Write a short story. Possible Story:  mom said, \"I have to play with my friend, but we are not a great friend.\" She says, \"It is very happy. It is a big and a big box. He is a good day. It is a big smile and',\n",
       " '<|begin_of_text|>Write a short story. Possible Story:  mom asked him to the dog. The end and said, \"It is not happy!\" Lily asks. She had many fun of her mom, and the dog were happy. She was very happy to go to the water, and the bird was sad',\n",
       " '<|begin_of_text|>Write a short story. Possible Story:  mom asked the park and they were playing with the ball. It wanted to be careful with a big tree. It was a big box in the park and ran to her. They went to the park and put on it in the park. They had',\n",
       " '<|begin_of_text|>Write a short story. Possible Story:  mom says that she was very proud of the dog. She was very sad. She had a small box in the sky. She had to see what it was. She said, \"Thank you, mom.\" Tim and they could not a lot of',\n",
       " '<|begin_of_text|>Write a short story. Possible Story:  dad was very sad. It was a good dog, and a big smile. The cat felt very happy. She said, \"I\\'m sorry, mom. You are not nice!\" He was very proud of the park. He had many friends.']"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "generate(\n",
    "    llama,\n",
    "    config=MASTER_CONFIG,\n",
    "    temperature=1.0,\n",
    "    top_k=5,\n",
    "    max_new_tokens=50,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "SimpleFeedForwardNN 74086944 Params | Train: 4.189 | Val: 4.471\n",
      "SimpleFeedForwardNN_RMS 74169888 Params | Train: 4.260 | Val: 4.492\n",
      "SimpleFeedForwardNN_RMS_Rope 76160832 Params | Train: 4.122 | Val: 4.216\n",
      "SimpleFeedForwardNN_RMS_RoPE_SwiGLU 76327297 Params | Train: 4.174 | Val: 4.575\n",
      "SimpleLlama 96444391 Params | Train: 10.516 | Val: 10.530\n",
      "SimpleLlama 335771145 Params | Train: 4.178 | Val: 4.442\n"
     ]
    }
   ],
   "source": [
    "for i in GLOBAL_KEEP_TRACK:\n",
    "    print(i)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Quantization"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "SimpleLlama size: 1280.865MB\n",
      "SimpleLlama size: 18.000MB\n"
     ]
    }
   ],
   "source": [
    "llama.to(\"cpu\")\n",
    "qconfig_dict = {\n",
    "    torch.nn.Embedding: torch.quantization.float_qparams_weight_only_qconfig,\n",
    "    torch.nn.Linear: torch.quantization.default_dynamic_qconfig,\n",
    "}\n",
    "# Post Training Dynamic Quantization\n",
    "dynamic_quantized_llama = torch.quantization.quantize_dynamic(\n",
    "    llama, qconfig_dict, dtype=torch.qint8\n",
    ")\n",
    "\n",
    "\n",
    "# Get Size difference\n",
    "def get_param_size(model):\n",
    "    param_size = 0\n",
    "    for param in model.parameters():\n",
    "        param_size += param.nelement() * param.element_size()\n",
    "    return param_size\n",
    "\n",
    "\n",
    "def get_buffer_size(model):\n",
    "    buffer_size = 0\n",
    "    for buffer in model.buffers():\n",
    "        buffer_size += buffer.nelement() * buffer.element_size()\n",
    "    return buffer_size\n",
    "\n",
    "\n",
    "def get_param_and_buffer_size(model):\n",
    "    param_size = get_param_size(model)\n",
    "    buffer_size = get_buffer_size(model)\n",
    "    return param_size, buffer_size\n",
    "\n",
    "\n",
    "def get_size_difference(models: list) -> str:\n",
    "    keeping_track = []\n",
    "    for idx, model in enumerate(models):\n",
    "        param_size, buffer_size = get_param_and_buffer_size(model)\n",
    "        size_all_mb = (param_size + buffer_size) / 1024**2\n",
    "        keeping_track.append(\n",
    "            f\"{type(model).__name__} size: {size_all_mb:.3f}MB\"\n",
    "        )\n",
    "    return keeping_track\n",
    "\n",
    "\n",
    "list_of_sizes = get_size_difference([llama, dynamic_quantized_llama])\n",
    "for size in list_of_sizes:\n",
    "    print(size)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# LoRA\n",
    "\n",
    "If you have this:\n",
    "\n",
    "<p>def forward(self, x):<br>\n",
    "    x = self.linear_1(x)<br>\n",
    "    x = F.relu(x)<br>\n",
    "    x = self.linear_2(x)<br>\n",
    "    return x<br></p>\n",
    "\n",
    "change it to this:\n",
    "\n",
    "<p>def forward(self, x):<br>\n",
    "    x = self.linear_1(x) + self.lora_1(x)<br>\n",
    "    x = F.relu(x)<br>\n",
    "    x = self.linear_2(x) + self.lora_2(x)<br>\n",
    "    return logits<br></p>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [],
   "source": [
    "class LoRALayer(nn.Module):\n",
    "    def __init__(self, in_dim, out_dim, rank, alpha):\n",
    "        super().__init__()\n",
    "        standard_deviation = 1 / torch.sqrt(torch.tensor(rank).float())\n",
    "        self.A = nn.Parameter(\n",
    "            torch.randn(in_dim, rank) * standard_deviation\n",
    "        )\n",
    "        self.B = nn.Parameter(torch.zeros(rank, out_dim))\n",
    "        self.alpha = alpha\n",
    "\n",
    "    def forward(self, x):\n",
    "        x = self.alpha * (x @ self.A @ self.B)\n",
    "        return x\n",
    "\n",
    "\n",
    "class LinearWithLoRA(nn.Module):\n",
    "    def __init__(self, linear, rank, alpha):\n",
    "        super().__init__()\n",
    "        self.linear = linear\n",
    "        self.lora = LoRALayer(\n",
    "            linear.in_features, linear.out_features, rank, alpha\n",
    "        )\n",
    "\n",
    "    def forward(self, x):\n",
    "        return self.linear(x) + self.lora(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [],
   "source": [
    "from functools import partial\n",
    "\n",
    "\n",
    "# Utils\n",
    "class LoRAParametrization(nn.Module):\n",
    "    def __init__(\n",
    "        self,\n",
    "        fan_in,\n",
    "        fan_out,\n",
    "        fan_in_fan_out=False,\n",
    "        rank=0,\n",
    "        lora_dropout_p=0.0,\n",
    "        lora_alpha=1,\n",
    "    ):\n",
    "        super().__init__()\n",
    "        self.swap = (\n",
    "            (lambda x: (x[1], x[0])) if fan_in_fan_out else (lambda x: x)\n",
    "        )\n",
    "        self.lora_A = nn.Parameter(torch.zeros(self.swap((rank, fan_in))))\n",
    "        self.lora_B = nn.Parameter(torch.zeros(self.swap((fan_out, rank))))\n",
    "        self.lora_alpha, self.rank = lora_alpha, rank\n",
    "        self.scaling = lora_alpha / rank\n",
    "        self.lora_dropout = (\n",
    "            nn.Dropout(p=lora_dropout_p)\n",
    "            if lora_dropout_p > 0\n",
    "            else lambda x: x\n",
    "        )\n",
    "        self.dropout_fn = (\n",
    "            self._dropout if lora_dropout_p > 0 else lambda x: x\n",
    "        )\n",
    "        self.register_buffer(\n",
    "            \"lora_dropout_mask\",\n",
    "            torch.ones(self.swap((1, fan_in)), dtype=self.lora_A.dtype),\n",
    "        )\n",
    "        self.forward_fn = self.lora_forward\n",
    "\n",
    "    def forward(self, X):\n",
    "        return self.forward_fn(X)\n",
    "\n",
    "    def lora_forward(self, X):\n",
    "        return (\n",
    "            X\n",
    "            + torch.matmul(\n",
    "                *self.swap((self.lora_B, self.dropout_fn(self.lora_A)))\n",
    "            ).view(X.shape)\n",
    "            * self.scaling\n",
    "        )\n",
    "\n",
    "    @classmethod\n",
    "    def lora_from_layer(\n",
    "        cls, layer, rank=0, lora_dropout_p=0.0, lora_alpha=1\n",
    "    ):\n",
    "        fan_out, fan_in = layer.weight.shape\n",
    "        return cls(\n",
    "            fan_in,\n",
    "            fan_out,\n",
    "            fan_in_fan_out=False,\n",
    "            rank=rank,\n",
    "            lora_dropout_p=lora_dropout_p,\n",
    "            lora_alpha=lora_alpha,\n",
    "        )\n",
    "\n",
    "\n",
    "lora_config = {\n",
    "    nn.Linear: {\n",
    "        \"weight\": partial(LoRAParametrization.lora_from_layer, rank=16),\n",
    "    },\n",
    "}\n",
    "\n",
    "\n",
    "def apply_lora(layer, register=True, merge=False, lora_config=lora_config):\n",
    "    if register:\n",
    "        if type(layer) in lora_config:\n",
    "            for attr_name, parametrization in lora_config[\n",
    "                type(layer)\n",
    "            ].items():\n",
    "                torch.nn.utils.parametrize.register_parametrization(\n",
    "                    layer, attr_name, parametrization(layer)\n",
    "                )\n",
    "    else:\n",
    "        if hasattr(layer, \"parametrizations\"):\n",
    "            for attr_name in layer.parametrizations.keys():\n",
    "                torch.nn.utils.parametrize.remove_parametrizations(\n",
    "                    layer, attr_name, leave_parametrized=merge\n",
    "                )\n",
    "\n",
    "\n",
    "def add_lora(model, lora_config=lora_config):\n",
    "    model.apply(partial(apply_lora, lora_config=lora_config))\n",
    "\n",
    "\n",
    "def remove_lora(model):\n",
    "    \"\"\"remove lora parametrization to all layers in a model. This will remove all parametrization\"\"\"\n",
    "    model.apply(partial(apply_lora, register=False, merge=False))\n",
    "\n",
    "\n",
    "def merge_lora(model):\n",
    "    model.apply(partial(apply_lora, register=False, merge=True))\n",
    "\n",
    "\n",
    "def name_is_lora(name):\n",
    "    return (\n",
    "        len(name.split(\".\")) >= 4\n",
    "        and (name.split(\".\")[-4]) == \"parametrizations\"\n",
    "        and name.split(\".\")[-1] in [\"lora_A\", \"lora_B\"]\n",
    "    )\n",
    "\n",
    "\n",
    "def get_params_by_name(model, print_shapes=False, name_filter=None):\n",
    "    for n, p in model.named_parameters():\n",
    "        if name_filter is None or name_filter(n):\n",
    "            if print_shapes:\n",
    "                print(n, p.shape)\n",
    "            yield p\n",
    "\n",
    "\n",
    "def get_lora_params(model, print_shapes=False):\n",
    "    return get_params_by_name(\n",
    "        model, print_shapes=print_shapes, name_filter=name_is_lora\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [],
   "source": [
    "# New Dataset for Lora\n",
    "dataset = load_dataset(\n",
    "    \"text\",\n",
    "    data_files={\n",
    "        \"train\": [\"../../data/Lima-train.csv\"],\n",
    "        \"val\": [\"../../data/Lima-test.csv\"],\n",
    "    },\n",
    "    streaming=True,\n",
    ")\n",
    "\n",
    "encoded_dataset = dataset.map(\n",
    "    lambda examples: tokenizer(\n",
    "        examples[\"text\"],\n",
    "        padding=True,\n",
    "        max_length=MASTER_CONFIG[\"context_window\"],\n",
    "        truncation=True,\n",
    "        return_tensors=\"pt\",\n",
    "    ),\n",
    "    batched=True,\n",
    ")\n",
    "train_data = iter(encoded_dataset[\"train\"].shuffle())\n",
    "val_data = iter(encoded_dataset[\"val\"].shuffle())\n",
    "train_data = cycle(train_data)\n",
    "val_data = cycle(val_data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Step 1: Add LoRA to trained model\n",
    "llama.to(\"cpu\")\n",
    "add_lora(llama)\n",
    "llama.to(device)\n",
    "\n",
    "# Step 2: Get the LoRA params instead of the whole model's\n",
    "parameters = [{\"params\": list(get_lora_params(llama))}]\n",
    "# Step 3: initialize optimizer with LoRA Params\n",
    "lora_optimizer = torch.optim.AdamW(parameters, lr=1e-3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 0 | train loss 11.704 | val loss 11.659 | Time 1.780 | ETA: 0:00:35.596399\n",
      "lr:  [1.0001209025544738e-05]\n",
      "Epoch 50 | train loss 11.306 | val loss 11.686 | Time 9.569 | ETA: 0:03:01.818796\n",
      "lr:  [1.3137956571267626e-05]\n",
      "Epoch 100 | train loss 11.123 | val loss 11.442 | Time 10.605 | ETA: 0:03:10.883361\n",
      "lr:  [2.223015032030447e-05]\n",
      "Epoch 150 | train loss 11.443 | val loss 11.938 | Time 10.544 | ETA: 0:02:59.244051\n",
      "lr:  [3.705391028729307e-05]\n",
      "Epoch 200 | train loss 10.970 | val loss 11.927 | Time 10.717 | ETA: 0:02:51.469467\n",
      "lr:  [5.7244226304607204e-05]\n",
      "Epoch 250 | train loss 11.119 | val loss 11.666 | Time 11.050 | ETA: 0:02:45.753322\n",
      "lr:  [8.230394578408373e-05]\n",
      "Epoch 300 | train loss 11.025 | val loss 11.472 | Time 11.066 | ETA: 0:02:34.919000\n",
      "lr:  [0.00011161601526369715]\n",
      "Epoch 350 | train loss 11.704 | val loss 12.027 | Time 11.155 | ETA: 0:02:25.020321\n",
      "lr:  [0.00014445867431167806]\n",
      "Epoch 400 | train loss 11.016 | val loss 11.805 | Time 11.017 | ETA: 0:02:12.204120\n",
      "lr:  [0.00018002322766372895]\n",
      "Epoch 450 | train loss 11.709 | val loss 11.609 | Time 11.086 | ETA: 0:02:01.944707\n",
      "lr:  [0.00021743395798433685]\n",
      "Epoch 500 | train loss 11.375 | val loss 11.448 | Time 9.945 | ETA: 0:01:39.454873\n",
      "lr:  [0.00025576968893391316]\n",
      "Epoch 550 | train loss 11.057 | val loss 12.132 | Time 10.035 | ETA: 0:01:30.317342\n",
      "lr:  [0.0002940864675874819]\n",
      "Epoch 600 | train loss 11.262 | val loss 11.729 | Time 10.031 | ETA: 0:01:20.251831\n",
      "lr:  [0.0003314408076884945]\n",
      "Epoch 650 | train loss 11.104 | val loss 11.548 | Time 9.978 | ETA: 0:01:09.843274\n",
      "lr:  [0.000366912921411718]\n",
      "Epoch 700 | train loss 11.058 | val loss 11.808 | Time 9.964 | ETA: 0:00:59.783522\n",
      "lr:  [0.0003996293675920899]\n",
      "Epoch 750 | train loss 10.990 | val loss 11.913 | Time 10.003 | ETA: 0:00:50.014831\n",
      "lr:  [0.0004287845587449898]\n",
      "Epoch 800 | train loss 11.641 | val loss 11.615 | Time 10.444 | ETA: 0:00:41.777612\n",
      "lr:  [0.0004536605973037015]\n",
      "Epoch 850 | train loss 11.092 | val loss 11.565 | Time 11.003 | ETA: 0:00:33.009245\n",
      "lr:  [0.0004736449526400705]\n",
      "Epoch 900 | train loss 11.679 | val loss 11.915 | Time 11.025 | ETA: 0:00:22.050917\n",
      "lr:  [0.0004882455436014427]\n",
      "Epoch 950 | train loss 11.439 | val loss 11.984 | Time 10.882 | ETA: 0:00:10.882483\n",
      "lr:  [0.0004971028551817645]\n",
      "training loss 11.439 | validation loss: 11.984\n"
     ]
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Step 4: Train\n",
    "train(\n",
    "    llama,\n",
    "    lora_optimizer,\n",
    "    scheduler,\n",
    "    data=train_data,\n",
    "    config=MASTER_CONFIG,\n",
    "    lora=True,\n",
    "    print_logs=True,\n",
    ")\n",
    "\n",
    "# Step 5: export the params\n",
    "state_dict = llama.state_dict()\n",
    "lora_state_dict = {k: v for k, v in state_dict.items() if name_is_lora(k)}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [],
   "source": [
    "torch.save(llama.state_dict(), \"./llama3/llama.pth\")\n",
    "torch.save(lora_state_dict, \"./llama3/lora.pth\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['\" and sees behaved each other and fly. They look mixed and had a daughter. They learned a nice box and the bottleaser. They were yii.',\n",
       " '\" his realized then he get pieces. Hisaffe is good box than him, and eat and sleep. Tom was careful and laugh, but they party,',\n",
       " '\". She wants to the ground, and water. She showed the shop truck, and everyone, a lion money off and sniff with it to cry again',\n",
       " '\",\" Mom said.  gun likes said with her worms. From goodbye, they were Max and angry. They found a big hoped, in herin',\n",
       " '\" hide. She was happy and kite and a ball. I will feel good drive to do\\'s not find his hand away. Tom and the table.']"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Loading and Inferencing with LoRA\n",
    "add_lora(llama)\n",
    "\n",
    "_ = llama.load_state_dict(lora_state_dict, strict=False)\n",
    "\n",
    "merge_lora(llama)\n",
    "\n",
    "generate(llama, lora=True)\n",
    "\n",
    "# remove_lora(llama)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "World size: -1\n"
     ]
    },
    {
     "ename": "ProcessRaisedException",
     "evalue": "\n\n-- Process 0 terminated with the following error:\nTraceback (most recent call last):\n  File \"c:\\Users\\chrisb\\.conda\\envs\\chapter_9\\lib\\site-packages\\torch\\multiprocessing\\spawn.py\", line 75, in _wrap\n    fn(i, *args)\n  File \"c:\\Users\\chrisb\\Documents\\GitHub\\LLMs-in-Production\\chapters\\chapter_9\\train_utils.py\", line 689, in fsdp_main\n    dist.init_process_group(\"nccl\", rank=rank, world_size=world_size)\n  File \"c:\\Users\\chrisb\\.conda\\envs\\chapter_9\\lib\\site-packages\\torch\\distributed\\c10d_logger.py\", line 75, in wrapper\n    return func(*args, **kwargs)\n  File \"c:\\Users\\chrisb\\.conda\\envs\\chapter_9\\lib\\site-packages\\torch\\distributed\\c10d_logger.py\", line 89, in wrapper\n    func_return = func(*args, **kwargs)\n  File \"c:\\Users\\chrisb\\.conda\\envs\\chapter_9\\lib\\site-packages\\torch\\distributed\\distributed_c10d.py\", line 1312, in init_process_group\n    default_pg, _ = _new_process_group_helper(\n  File \"c:\\Users\\chrisb\\.conda\\envs\\chapter_9\\lib\\site-packages\\torch\\distributed\\distributed_c10d.py\", line 1513, in _new_process_group_helper\n    raise RuntimeError(\"Distributed package doesn't have NCCL built in\")\nRuntimeError: Distributed package doesn't have NCCL built in\n",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mProcessRaisedException\u001b[0m                    Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[47], line 14\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mtrain_utils\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m FSDP_QLORA\n\u001b[0;32m      3\u001b[0m trainer \u001b[38;5;241m=\u001b[39m FSDP_QLORA(\n\u001b[0;32m      4\u001b[0m     model_name\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmeta-llama/Llama-2-13b-hf\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[0;32m      5\u001b[0m     batch_size\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m2\u001b[39m,\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m     11\u001b[0m     reentrant_checkpointing\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m,\n\u001b[0;32m     12\u001b[0m )\n\u001b[1;32m---> 14\u001b[0m \u001b[43mtrainer\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtrain_qlora\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[1;32mc:\\Users\\chrisb\\Documents\\GitHub\\LLMs-in-Production\\chapters\\chapter_9\\train_utils.py:1449\u001b[0m, in \u001b[0;36mFSDP_QLORA.train_qlora\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m   1448\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mtrain_qlora\u001b[39m(\u001b[38;5;28mself\u001b[39m):\n\u001b[1;32m-> 1449\u001b[0m     \u001b[43mmp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mspawn\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m   1450\u001b[0m \u001b[43m        \u001b[49m\u001b[43mfsdp_main\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1451\u001b[0m \u001b[43m        \u001b[49m\u001b[43margs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mworld_size\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1452\u001b[0m \u001b[43m        \u001b[49m\u001b[43mnprocs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mtorch\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcuda\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdevice_count\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1453\u001b[0m \u001b[43m        \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[0;32m   1454\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[1;32mc:\\Users\\chrisb\\.conda\\envs\\chapter_9\\lib\\site-packages\\torch\\multiprocessing\\spawn.py:281\u001b[0m, in \u001b[0;36mspawn\u001b[1;34m(fn, args, nprocs, join, daemon, start_method)\u001b[0m\n\u001b[0;32m    275\u001b[0m     msg \u001b[38;5;241m=\u001b[39m (\n\u001b[0;32m    276\u001b[0m         \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mThis method only supports start_method=spawn (got: \u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;124m).\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m    277\u001b[0m         \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mTo use a different start_method use:\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m    278\u001b[0m         \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m torch.multiprocessing.start_processes(...)\u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;241m%\u001b[39m start_method\n\u001b[0;32m    279\u001b[0m     )\n\u001b[0;32m    280\u001b[0m     warnings\u001b[38;5;241m.\u001b[39mwarn(msg)\n\u001b[1;32m--> 281\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mstart_processes\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfn\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnprocs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdaemon\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstart_method\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mspawn\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n",
      "File \u001b[1;32mc:\\Users\\chrisb\\.conda\\envs\\chapter_9\\lib\\site-packages\\torch\\multiprocessing\\spawn.py:237\u001b[0m, in \u001b[0;36mstart_processes\u001b[1;34m(fn, args, nprocs, join, daemon, start_method)\u001b[0m\n\u001b[0;32m    234\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m context\n\u001b[0;32m    236\u001b[0m \u001b[38;5;66;03m# Loop on join until it returns True or raises an exception.\u001b[39;00m\n\u001b[1;32m--> 237\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[43mcontext\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m:\n\u001b[0;32m    238\u001b[0m     \u001b[38;5;28;01mpass\u001b[39;00m\n",
      "File \u001b[1;32mc:\\Users\\chrisb\\.conda\\envs\\chapter_9\\lib\\site-packages\\torch\\multiprocessing\\spawn.py:188\u001b[0m, in \u001b[0;36mProcessContext.join\u001b[1;34m(self, timeout)\u001b[0m\n\u001b[0;32m    186\u001b[0m msg \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124m-- Process \u001b[39m\u001b[38;5;132;01m%d\u001b[39;00m\u001b[38;5;124m terminated with the following error:\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;241m%\u001b[39m error_index\n\u001b[0;32m    187\u001b[0m msg \u001b[38;5;241m+\u001b[39m\u001b[38;5;241m=\u001b[39m original_trace\n\u001b[1;32m--> 188\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m ProcessRaisedException(msg, error_index, failed_process\u001b[38;5;241m.\u001b[39mpid)\n",
      "\u001b[1;31mProcessRaisedException\u001b[0m: \n\n-- Process 0 terminated with the following error:\nTraceback (most recent call last):\n  File \"c:\\Users\\chrisb\\.conda\\envs\\chapter_9\\lib\\site-packages\\torch\\multiprocessing\\spawn.py\", line 75, in _wrap\n    fn(i, *args)\n  File \"c:\\Users\\chrisb\\Documents\\GitHub\\LLMs-in-Production\\chapters\\chapter_9\\train_utils.py\", line 689, in fsdp_main\n    dist.init_process_group(\"nccl\", rank=rank, world_size=world_size)\n  File \"c:\\Users\\chrisb\\.conda\\envs\\chapter_9\\lib\\site-packages\\torch\\distributed\\c10d_logger.py\", line 75, in wrapper\n    return func(*args, **kwargs)\n  File \"c:\\Users\\chrisb\\.conda\\envs\\chapter_9\\lib\\site-packages\\torch\\distributed\\c10d_logger.py\", line 89, in wrapper\n    func_return = func(*args, **kwargs)\n  File \"c:\\Users\\chrisb\\.conda\\envs\\chapter_9\\lib\\site-packages\\torch\\distributed\\distributed_c10d.py\", line 1312, in init_process_group\n    default_pg, _ = _new_process_group_helper(\n  File \"c:\\Users\\chrisb\\.conda\\envs\\chapter_9\\lib\\site-packages\\torch\\distributed\\distributed_c10d.py\", line 1513, in _new_process_group_helper\n    raise RuntimeError(\"Distributed package doesn't have NCCL built in\")\nRuntimeError: Distributed package doesn't have NCCL built in\n"
     ]
    }
   ],
   "source": [
    "from train_utils import FSDP_QLORA\n",
    "\n",
    "#NOTE FSDP will not work on Windows or Mac for the foreseeable future, NCCL is currently Linux-only (or WSL)\n",
    "trainer = FSDP_QLORA(\n",
    "    model_name=\"meta-llama/Llama-2-13b-hf\",\n",
    "    batch_size=2,\n",
    "    context_length=2048,\n",
    "    precision=\"bf16\",\n",
    "    train_type=\"qlora\",\n",
    "    use_gradient_checkpointing=True,\n",
    "    dataset=\"alpaca\",\n",
    "    reentrant_checkpointing=True,\n",
    ")\n",
    "\n",
    "trainer.train_qlora()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from safetensors import safe_open\n",
    "import torch\n",
    "from transformers import LlamaForCausalLM, BitsAndBytesConfig\n",
    "from peft import get_peft_config, get_peft_model, LoraConfig, TaskType\n",
    "\n",
    "tensors = {}\n",
    "with safe_open(\n",
    "    \"qlora_output/model_state_dict.safetensors\", framework=\"pt\", device=0\n",
    ") as f:\n",
    "    for k in f.keys():\n",
    "        tensors[k] = f.get_tensor(k)\n",
    "\n",
    "for k in tensors:\n",
    "    if \"lora\" not in k:\n",
    "        tensors[k] = None\n",
    "\n",
    "bnb_config = BitsAndBytesConfig(\n",
    "    load_in_4bit=True,\n",
    "    bnb_4bit_quant_type=\"nf4\",\n",
    "    bnb_4bit_use_double_quant=False,\n",
    "    bnb_4bit_compute_dtype=torch.bfloat16,\n",
    ")\n",
    "model = LlamaForCausalLM.from_pretrained(\n",
    "    \"meta-llama/Llama-2-7b-hf\",\n",
    "    use_cache=False,\n",
    "    quantization_config=bnb_config,\n",
    ")\n",
    "\n",
    "for param in model.parameters():\n",
    "    param.requires_grad = False\n",
    "\n",
    "peft_config = LoraConfig(\n",
    "    task_type=TaskType.CAUSAL_LM,\n",
    "    inference_mode=False,\n",
    "    r=64,\n",
    "    lora_alpha=16,\n",
    "    lora_dropout=0.1,\n",
    "    target_modules=[\n",
    "        \"k_proj\",\n",
    "        \"q_proj\",\n",
    "        \"v_proj\",\n",
    "        \"up_proj\",\n",
    "        \"down_proj\",\n",
    "        \"gate_proj\",\n",
    "    ],\n",
    ")\n",
    "model = get_peft_model(model, peft_config)\n",
    "\n",
    "list(model.state_dict().keys())[:10]\n",
    "\n",
    "new_sd = model.state_dict()\n",
    "for k in new_sd:\n",
    "    if \"lora\" in k:\n",
    "        new_sd[k] = tensors[k]\n",
    "\n",
    "model.load_state_dict(new_sd)\n",
    "\n",
    "model.save_pretrained(\"./llama3/lora_adapters\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create a Gradio app that looks like this:\n",
    "\n",
    "# # Space will need your token to request hardware: set it as a Secret !\n",
    "# HF_TOKEN = os.environ.get(\"HF_TOKEN\")\n",
    "\n",
    "# # Space own repo_id\n",
    "# TRAINING_SPACE_ID = \"your_username/your_repo\"\n",
    "\n",
    "# from huggingface_hub import HfApi, SpaceHardware\n",
    "# api = HfApi(token=HF_TOKEN)\n",
    "\n",
    "# # On Space startup, check if a task is scheduled. If yes, finetune the model. If not,\n",
    "# # display an interface to request a new task.\n",
    "# task = get_task()\n",
    "# if task is None:\n",
    "#     # Start Gradio app\n",
    "#     def gradio_fn(task):\n",
    "#         # On user request, add task and request hardware\n",
    "#         add_task(task)\n",
    "#         api.request_space_hardware(repo_id=TRAINING_SPACE_ID, hardware=SpaceHardware.CPU_BASIC)\n",
    "\n",
    "#     gr.Interface(fn=gradio_fn, ...).launch()\n",
    "# else:\n",
    "#     runtime = api.get_space_runtime(repo_id=TRAINING_SPACE_ID)\n",
    "#     # Check if Space is loaded with a GPU.\n",
    "#     if runtime.hardware == SpaceHardware.CPU_BASIC:\n",
    "#         # If yes, finetune base model on dataset !\n",
    "#         chat_with_user(task)\n",
    "\n",
    "#         # Then, mark the task as \"DONE\"\n",
    "#         mark_as_done(task)\n",
    "\n",
    "#         # DO NOT FORGET: set back CPU hardware\n",
    "#         api.request_space_hardware(repo_id=TRAINING_SPACE_ID, hardware=SpaceHardware.CPU_BASIC)\n",
    "#     else:\n",
    "#         api.request_space_hardware(repo_id=TRAINING_SPACE_ID, hardware=SpaceHardware.CPU_BASIC)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%pip install huggingface_hub -q\n",
    "\n",
    "from huggingface_hub import notebook_login, HfApi\n",
    "\n",
    "notebook_login()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "api = HfApi()\n",
    "api.create_repo(\n",
    "    repo_id=\"your_username/your_repo\", repo_type=\"space\", space_sdk=\"gradio\"\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "stuff_to_save = [\n",
    "    \"llama.pth\",\n",
    "    \"lora.pth\",\n",
    "    \"special_tokens_map.json\",\n",
    "    \"tokenizer_config.json\",\n",
    "    \"tokenizer.json\",\n",
    "    \"tokenizer.model\",\n",
    "    \"gradio_app.py\",\n",
    "]\n",
    "for thing in stuff_to_save:\n",
    "    api.upload_file(\n",
    "        path_or_fileobj=f\"./llama3/{thing}\",\n",
    "        path_in_repo=thing,\n",
    "        repo_id=\"your_username/your_repo\",\n",
    "        repo_type=\"space\",\n",
    "    )"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "llmbook",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
